Merge "Add santoscordon@ to PowerManager OWNERS"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..45884c4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/.idea
+*.iml
diff --git a/Android.bp b/Android.bp
index cbc67d0..32bd408 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,6 +104,7 @@
"core/java/android/app/backup/IRestoreObserver.aidl",
"core/java/android/app/backup/IRestoreSession.aidl",
"core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+ "core/java/android/app/role/IOnRoleHoldersChangedListener.aidl",
"core/java/android/app/role/IRoleManager.aidl",
"core/java/android/app/role/IRoleManagerCallback.aidl",
"core/java/android/app/slice/ISliceManager.aidl",
@@ -162,6 +163,7 @@
"core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
+ "core/java/android/hardware/display/IColorDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
"core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
@@ -264,9 +266,12 @@
"core/java/android/os/storage/IStorageEventListener.aidl",
"core/java/android/os/storage/IStorageShutdownObserver.aidl",
"core/java/android/os/storage/IObbActionListener.aidl",
+ "core/java/android/permission/IRuntimePermissionPresenter.aidl",
"core/java/android/rolecontrollerservice/IRoleControllerService.aidl",
":keystore_aidl",
"core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
+ "core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl",
+ "core/java/android/service/autofill/augmented/IFillCallback.aidl",
"core/java/android/service/autofill/IAutoFillService.aidl",
"core/java/android/service/autofill/IAutofillFieldClassificationService.aidl",
"core/java/android/service/autofill/IFillCallback.aidl",
@@ -289,7 +294,7 @@
"core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
"core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
"core/java/android/service/gatekeeper/IGateKeeperService.aidl",
- "core/java/android/service/intelligence/IIntelligenceService.aidl",
+ "core/java/android/service/contentcapture/IContentCaptureService.aidl",
"core/java/android/service/notification/INotificationListener.aidl",
"core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
"core/java/android/service/notification/IConditionListener.aidl",
@@ -350,7 +355,7 @@
"core/java/android/view/autofill/IAutoFillManagerClient.aidl",
"core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
"core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
- "core/java/android/view/intelligence/IIntelligenceManager.aidl",
+ "core/java/android/view/contentcapture/IContentCaptureManager.aidl",
"core/java/android/view/IApplicationToken.aidl",
"core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
"core/java/android/view/IDockedStackListener.aidl",
@@ -439,7 +444,6 @@
"location/java/android/location/IGeocodeProvider.aidl",
"location/java/android/location/IGeofenceProvider.aidl",
"location/java/android/location/IGnssStatusListener.aidl",
- "location/java/android/location/IGnssStatusProvider.aidl",
"location/java/android/location/IGnssMeasurementsListener.aidl",
"location/java/android/location/IGnssNavigationMessageListener.aidl",
"location/java/android/location/ILocationListener.aidl",
@@ -448,6 +452,7 @@
"location/java/android/location/IGpsGeofenceHardware.aidl",
"location/java/android/location/INetInitiatedListener.aidl",
"location/java/com/android/internal/location/ILocationProvider.aidl",
+ "location/java/com/android/internal/location/ILocationProviderManager.aidl",
"media/java/android/media/IAudioFocusDispatcher.aidl",
"media/java/android/media/IAudioRoutesObserver.aidl",
"media/java/android/media/IAudioService.aidl",
@@ -572,6 +577,7 @@
"telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
"telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
"telephony/java/com/android/internal/telephony/IMms.aidl",
+ "telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl",
"telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
@@ -629,7 +635,6 @@
":libupdate_engine_aidl",
":storaged_aidl",
- ":netd_aidl",
":vold_aidl",
":installd_aidl",
":dumpstate_aidl",
@@ -652,6 +657,8 @@
"core/java/com/android/server/DropboxLogTags.logtags",
"core/java/org/chromium/arc/EventLogTags.logtags",
+ ":platform-properties",
+
":framework-statslog-gen",
],
@@ -684,7 +691,6 @@
"frameworks/av/media/libaudioclient/aidl",
"frameworks/native/aidl/gui",
"system/core/storaged/binder",
- "system/netd/server/binder",
"system/vold/binder",
"system/bt/binder",
"system/security/keystore/binder",
@@ -728,6 +734,7 @@
"android.hardware.radio-V1.4-java",
"android.hardware.usb.gadget-V1.0-java",
"netd_aidl_interface-java",
+ "devicepolicyprotosnano",
],
required: [
@@ -777,16 +784,6 @@
],
}
-// A host library containing the inspector annotations for inspector-annotation-processor.
-java_library_host {
- name: "inspector-annotation",
- srcs: [
- "core/java/android/view/inspector/InspectableChildren.java",
- "core/java/android/view/inspector/InspectableNodeName.java",
- "core/java/android/view/inspector/InspectableProperty.java",
- ],
-}
-
// A host library including just UnsupportedAppUsage.java so that the annotation
// processor can also use this annotation.
java_library_host {
@@ -1608,6 +1605,7 @@
],
dex_mapping_filename: "dex-mapping.txt",
args: metalava_framework_docs_args +
+ " --hide ReferencesHidden " +
" --show-unannotated " +
" --show-annotation android.annotation.SystemApi " +
" --show-annotation android.annotation.TestApi "
diff --git a/Android.mk b/Android.mk
index b7dda9a..92e33e9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,10 +76,9 @@
.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- frameworks/base/config/hiddenapi-light-greylist.txt \
- frameworks/base/config/hiddenapi-vendor-list.txt \
+ frameworks/base/config/hiddenapi-greylist.txt \
+ frameworks/base/config/hiddenapi-greylist-max-p.txt \
frameworks/base/config/hiddenapi-greylist-max-o.txt \
- frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
frameworks/base/config/hiddenapi-force-blacklist.txt \
$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
@@ -88,10 +87,9 @@
--public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
--private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
--csv $(PRIVATE_FLAGS_INPUTS) \
- --greylist \
- frameworks/base/config/hiddenapi-light-greylist.txt \
- frameworks/base/config/hiddenapi-vendor-list.txt \
+ --greylist frameworks/base/config/hiddenapi-greylist.txt \
--greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
+ --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
--greylist-max-o-ignore-conflicts \
frameworks/base/config/hiddenapi-greylist-max-o.txt \
--blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 5be0cb0..99e4ba1 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -39,6 +39,7 @@
@LargeTest
public class BinderCallsStatsPerfTest {
private static final int DEFAULT_BUCKET_SIZE = 1000;
+ private static final int WORKSOURCE_UID = 1;
static class FakeCpuTimeBinderCallsStats extends BinderCallsStats {
private int mTimeMs;
@@ -117,8 +118,8 @@
Binder b = new Binder();
while (state.keepRunning()) {
for (int i = 0; i < 10000; i++) {
- CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize);
- mBinderCallsStats.callEnded(s, 0, 0);
+ CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize, WORKSOURCE_UID);
+ mBinderCallsStats.callEnded(s, 0, 0, WORKSOURCE_UID);
}
}
}
diff --git a/api/current.txt b/api/current.txt
index 3967896..f1f3789 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -78,6 +78,7 @@
field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
+ field public static final java.lang.String GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY = "android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
@@ -90,6 +91,7 @@
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
+ field public static final java.lang.String CALL_COMPANION_APP = "android.permission.CALL_COMPANION_APP";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -644,6 +646,7 @@
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
+ field public static final int foregroundServiceType = 16844191; // 0x101059f
field public static final int foregroundTint = 16843885; // 0x101046d
field public static final int foregroundTintMode = 16843886; // 0x101046e
field public static final int format = 16843013; // 0x1010105
@@ -701,6 +704,7 @@
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
field public static final int hardwareAccelerated = 16843475; // 0x10102d3
field public static final int hasCode = 16842764; // 0x101000c
+ field public static final int hasFragileUserData = 16844192; // 0x10105a0
field public static final deprecated int headerAmPmTextAppearance = 16843936; // 0x10104a0
field public static final int headerBackground = 16843055; // 0x101012f
field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
@@ -5947,6 +5951,15 @@
field public static final int STYLE_SPINNER = 0; // 0x0
}
+ public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
+ ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction);
+ method public int describeContents();
+ method public android.app.RemoteAction getUserAction();
+ method public java.lang.CharSequence getUserMessage();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
+ }
+
public final class RemoteAction implements android.os.Parcelable {
ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
@@ -5972,6 +5985,7 @@
method public java.util.Set<java.lang.String> getAllowedDataTypes();
method public java.lang.CharSequence[] getChoices();
method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
+ method public int getEditChoicesBeforeSending();
method public android.os.Bundle getExtras();
method public java.lang.CharSequence getLabel();
method public java.lang.String getResultKey();
@@ -5981,6 +5995,9 @@
method public static void setResultsSource(android.content.Intent, int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
field public static final int SOURCE_CHOICE = 1; // 0x1
@@ -5995,6 +6012,7 @@
method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+ method public android.app.RemoteInput.Builder setEditChoicesBeforeSending(int);
method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
}
@@ -6432,6 +6450,13 @@
field public static final android.os.Parcelable.Creator<android.app.admin.ConnectEvent> CREATOR;
}
+ public class DelegatedAdminReceiver extends android.content.BroadcastReceiver {
+ ctor public DelegatedAdminReceiver();
+ method public java.lang.String onChoosePrivateKeyAlias(android.content.Context, android.content.Intent, int, android.net.Uri, java.lang.String);
+ method public void onNetworkLogsAvailable(android.content.Context, android.content.Intent, long, int);
+ method public void onReceive(android.content.Context, android.content.Intent);
+ }
+
public final class DeviceAdminInfo implements android.os.Parcelable {
ctor public DeviceAdminInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
@@ -6494,11 +6519,13 @@
method public void onUserStarted(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onUserStopped(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onUserSwitched(android.content.Context, android.content.Intent, android.os.UserHandle);
+ field public static final java.lang.String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
field public static final java.lang.String ACTION_LOCK_TASK_ENTERING = "android.app.action.LOCK_TASK_ENTERING";
field public static final java.lang.String ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING";
+ field public static final java.lang.String ACTION_NETWORK_LOGS_AVAILABLE = "android.app.action.NETWORK_LOGS_AVAILABLE";
field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED";
field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING";
field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
@@ -6572,6 +6599,7 @@
method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName);
method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
+ method public int getPasswordComplexity();
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -6664,7 +6692,7 @@
method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
- method public void setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
+ method public int setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
@@ -6743,10 +6771,13 @@
field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
+ field public static final java.lang.String DELEGATION_CERT_SELECTION = "delegation-cert-selection";
field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
field public static final java.lang.String DELEGATION_INSTALL_EXISTING_PACKAGE = "delegation-install-existing-package";
field public static final java.lang.String DELEGATION_KEEP_UNINSTALLED_PACKAGES = "delegation-keep-uninstalled-packages";
+ field public static final java.lang.String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
+ field public static final java.lang.String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation";
field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
@@ -6825,6 +6856,10 @@
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+ field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000
+ field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000
+ field public static final int PASSWORD_COMPLEXITY_MEDIUM = 196608; // 0x30000
+ field public static final int PASSWORD_COMPLEXITY_NONE = 0; // 0x0
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
@@ -6845,6 +6880,9 @@
field public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2; // 0x2
field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
field public static final int PRIVATE_DNS_MODE_UNKNOWN = 0; // 0x0
+ field public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2; // 0x2
+ field public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1; // 0x1
+ field public static final int PRIVATE_DNS_SET_SUCCESS = 0; // 0x0
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -7610,13 +7648,16 @@
method public java.lang.String getPackageName();
method public java.lang.String getShortcutId();
method public long getTimeStamp();
+ field public static final int ACTIVITY_PAUSED = 2; // 0x2
+ field public static final int ACTIVITY_RESUMED = 1; // 0x1
+ field public static final int ACTIVITY_STOPPED = 23; // 0x17
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
field public static final int FOREGROUND_SERVICE_START = 19; // 0x13
field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14
field public static final int KEYGUARD_HIDDEN = 18; // 0x12
field public static final int KEYGUARD_SHOWN = 17; // 0x11
- field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
- field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
+ field public static final deprecated int MOVE_TO_BACKGROUND = 2; // 0x2
+ field public static final deprecated int MOVE_TO_FOREGROUND = 1; // 0x1
field public static final int NONE = 0; // 0x0
field public static final int SCREEN_INTERACTIVE = 15; // 0xf
field public static final int SCREEN_NON_INTERACTIVE = 16; // 0x10
@@ -7633,9 +7674,11 @@
method public long getLastTimeForegroundServiceUsed();
method public long getLastTimeStamp();
method public long getLastTimeUsed();
+ method public long getLastTimeVisible();
method public java.lang.String getPackageName();
method public long getTotalTimeForegroundServiceUsed();
method public long getTotalTimeInForeground();
+ method public long getTotalTimeVisible();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.usage.UsageStats> CREATOR;
}
@@ -7692,6 +7735,7 @@
method protected void prepareView(android.view.View);
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
method public void setExecutor(java.util.concurrent.Executor);
+ method public void setOnLightBackground(boolean);
method public void updateAppWidget(android.widget.RemoteViews);
method public void updateAppWidgetOptions(android.os.Bundle);
method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
@@ -9226,11 +9270,31 @@
field public static final android.os.Parcelable.Creator<android.content.ComponentName> CREATOR;
}
- public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+ public abstract interface ContentInterface {
+ method public abstract android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
+ method public abstract int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
+ method public abstract android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
+ method public abstract android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+ method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
+ method public abstract java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
+ method public abstract java.lang.String getType(android.net.Uri) throws android.os.RemoteException;
+ method public abstract android.net.Uri insert(android.net.Uri, android.content.ContentValues) throws android.os.RemoteException;
+ method public abstract android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public abstract android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public abstract android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal) throws android.os.RemoteException;
+ method public abstract boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal) throws android.os.RemoteException;
+ method public abstract android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
+ method public abstract int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
+ }
+
+ public abstract class ContentProvider implements android.content.ComponentCallbacks2 android.content.ContentInterface {
ctor public ContentProvider();
+ method public android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
method public void attachInfo(android.content.Context, android.content.pm.ProviderInfo);
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]);
+ method public android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle);
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
method public android.net.Uri canonicalize(android.net.Uri);
method public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
@@ -9277,10 +9341,12 @@
method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
}
- public class ContentProviderClient implements java.lang.AutoCloseable {
+ public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
+ method public android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
+ method public android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
method public void close();
method public static void closeQuietly(android.content.ContentProviderClient);
@@ -9293,6 +9359,7 @@
method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException, android.os.RemoteException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
@@ -9357,7 +9424,7 @@
method public void setKeepUpdated(boolean);
}
- public abstract class ContentResolver {
+ public abstract class ContentResolver implements android.content.ContentInterface {
ctor public ContentResolver(android.content.Context);
method public final android.content.ContentProviderClient acquireContentProviderClient(android.net.Uri);
method public final android.content.ContentProviderClient acquireContentProviderClient(java.lang.String);
@@ -9368,11 +9435,13 @@
method public android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
method public final int bulkInsert(android.net.Uri, android.content.ContentValues[]);
method public final android.os.Bundle call(android.net.Uri, java.lang.String, java.lang.String, android.os.Bundle);
+ method public final android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle);
method public deprecated void cancelSync(android.net.Uri);
method public static void cancelSync(android.accounts.Account, java.lang.String);
method public static void cancelSync(android.content.SyncRequest);
method public final android.net.Uri canonicalize(android.net.Uri);
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+ method public android.os.Bundle getCache(android.net.Uri);
method public static deprecated android.content.SyncInfo getCurrentSync();
method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
method public static int getIsSyncable(android.accounts.Account, java.lang.String);
@@ -9391,15 +9460,19 @@
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
+ method public final android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public final android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final java.io.InputStream openInputStream(android.net.Uri) throws java.io.FileNotFoundException;
method public final java.io.OutputStream openOutputStream(android.net.Uri) throws java.io.FileNotFoundException;
method public final java.io.OutputStream openOutputStream(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public void putCache(android.net.Uri, android.os.Bundle);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -10204,6 +10277,7 @@
field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final java.lang.String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -11200,6 +11274,7 @@
method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
method public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
+ method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions();
method public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
@@ -11238,6 +11313,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public int getParentSessionId();
method public boolean isMultiPackage();
+ method public boolean isStaged();
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
@@ -11276,6 +11352,7 @@
method public boolean isActive();
method public boolean isMultiPackage();
method public boolean isSealed();
+ method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
@@ -11294,6 +11371,7 @@
method public void setOriginatingUri(android.net.Uri);
method public void setReferrerUri(android.net.Uri);
method public void setSize(long);
+ method public void setStaged();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR;
field public static final int MODE_FULL_INSTALL = 1; // 0x1
@@ -11478,6 +11556,7 @@
field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
+ field public static final java.lang.String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
@@ -11723,12 +11802,20 @@
ctor public ServiceInfo(android.content.pm.ServiceInfo);
method public int describeContents();
method public void dump(android.util.Printer, java.lang.String);
+ method public int getForegroundServiceType();
field public static final android.os.Parcelable.Creator<android.content.pm.ServiceInfo> CREATOR;
field public static final int FLAG_EXTERNAL_SERVICE = 4; // 0x4
field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
+ field public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5; // 0x5
+ field public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4; // 0x4
+ field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2; // 0x2
+ field public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6; // 0x6
+ field public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3; // 0x3
+ field public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1; // 0x1
+ field public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0; // 0x0
field public int flags;
field public java.lang.String permission;
}
@@ -13728,6 +13815,7 @@
public abstract class ColorSpace {
method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[]);
method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[], android.graphics.ColorSpace.Adaptation);
+ method public static float[] cctToIlluminantdXyz(int);
method public static float[] chromaticAdaptation(android.graphics.ColorSpace.Adaptation, float[], float[]);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
@@ -15679,6 +15767,7 @@
public static class MeasuredText.Builder {
ctor public MeasuredText.Builder(char[]);
+ ctor public MeasuredText.Builder(android.graphics.text.MeasuredText);
method public android.graphics.text.MeasuredText.Builder appendReplacementRun(android.graphics.Paint, int, float);
method public android.graphics.text.MeasuredText.Builder appendStyleRun(android.graphics.Paint, int, boolean);
method public android.graphics.text.MeasuredText build();
@@ -16497,6 +16586,7 @@
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
+ method public boolean isSessionConfigurationSupported(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
field public static final int TEMPLATE_MANUAL = 6; // 0x6
field public static final int TEMPLATE_PREVIEW = 1; // 0x1
field public static final int TEMPLATE_RECORD = 3; // 0x3
@@ -17094,8 +17184,9 @@
field public static final int RED = 0; // 0x0
}
- public final class SessionConfiguration {
+ public final class SessionConfiguration implements android.os.Parcelable {
ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback);
+ method public int describeContents();
method public java.util.concurrent.Executor getExecutor();
method public android.hardware.camera2.params.InputConfiguration getInputConfiguration();
method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
@@ -17104,6 +17195,8 @@
method public android.hardware.camera2.CameraCaptureSession.StateCallback getStateCallback();
method public void setInputConfiguration(android.hardware.camera2.params.InputConfiguration);
method public void setSessionParameters(android.hardware.camera2.CaptureRequest);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.SessionConfiguration> CREATOR;
field public static final int SESSION_HIGH_SPEED = 1; // 0x1
field public static final int SESSION_REGULAR = 0; // 0x0
}
@@ -22695,8 +22788,8 @@
method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
- method public void clearTestProviderEnabled(java.lang.String);
- method public void clearTestProviderLocation(java.lang.String);
+ method public deprecated void clearTestProviderEnabled(java.lang.String);
+ method public deprecated void clearTestProviderLocation(java.lang.String);
method public deprecated void clearTestProviderStatus(java.lang.String);
method public java.util.List<java.lang.String> getAllProviders();
method public java.lang.String getBestProvider(android.location.Criteria, boolean);
@@ -22774,9 +22867,10 @@
ctor public SettingInjectorService(java.lang.String);
method public final android.os.IBinder onBind(android.content.Intent);
method protected abstract boolean onGetEnabled();
- method protected abstract deprecated java.lang.String onGetSummary();
+ method protected abstract java.lang.String onGetSummary();
method public final void onStart(android.content.Intent, int);
method public final int onStartCommand(android.content.Intent, int, int);
+ method public static final void refreshSettings(android.content.Context);
field public static final java.lang.String ACTION_INJECTED_SETTING_CHANGED = "android.location.InjectedSettingChanged";
field public static final java.lang.String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService";
field public static final java.lang.String ATTRIBUTES_NAME = "injected-location-setting";
@@ -22908,6 +23002,7 @@
method public int getChannelIndexMask();
method public int getChannelMask();
method public int getEncoding();
+ method public int getFrameSizeInBytes();
method public int getSampleRate();
method public void writeToParcel(android.os.Parcel, int);
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -23010,6 +23105,7 @@
method public deprecated boolean isBluetoothA2dpOn();
method public boolean isBluetoothScoAvailableOffCall();
method public boolean isBluetoothScoOn();
+ method public static boolean isHapticPlaybackSupported();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public static boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
@@ -23347,6 +23443,7 @@
method public int getStreamType();
method public boolean getTimestamp(android.media.AudioTimestamp);
method public int getUnderrunCount();
+ method public static boolean isDirectPlaybackSupported(android.media.AudioFormat, android.media.AudioAttributes);
method public void pause() throws java.lang.IllegalStateException;
method public void play() throws java.lang.IllegalStateException;
method public void registerStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
@@ -23435,6 +23532,17 @@
method public void onTearDown(android.media.AudioTrack);
}
+ public class CallbackDataSourceDesc extends android.media.DataSourceDesc {
+ method public android.media.DataSourceCallback getDataSourceCallback();
+ }
+
+ public static class CallbackDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase {
+ ctor public CallbackDataSourceDesc.Builder();
+ ctor public CallbackDataSourceDesc.Builder(android.media.CallbackDataSourceDesc);
+ method public android.media.CallbackDataSourceDesc build();
+ method public android.media.CallbackDataSourceDesc.Builder setDataSource(android.media.DataSourceCallback);
+ }
+
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
@@ -23487,6 +23595,26 @@
field public static final int QUALITY_MEDIUM = 1; // 0x1
}
+ public abstract class DataSourceCallback implements java.io.Closeable {
+ ctor public DataSourceCallback();
+ method public abstract long getSize() throws java.io.IOException;
+ method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
+ }
+
+ public class DataSourceDesc {
+ method public long getEndPosition();
+ method public java.lang.String getMediaId();
+ method public long getStartPosition();
+ field public static final long LONG_MAX_TIME_MS = 576460752303423L; // 0x20c49ba5e353fL
+ field public static final long POSITION_UNKNOWN = 576460752303423L; // 0x20c49ba5e353fL
+ }
+
+ protected static class DataSourceDesc.BuilderBase<T extends android.media.DataSourceDesc.BuilderBase> {
+ method public T setEndPosition(long);
+ method public T setMediaId(java.lang.String);
+ method public T setStartPosition(long);
+ }
+
public final class DeniedByServerException extends android.media.MediaDrmException {
ctor public DeniedByServerException(java.lang.String);
}
@@ -23687,6 +23815,21 @@
field public static final int EULER_Z = 2; // 0x2
}
+ public class FileDataSourceDesc extends android.media.DataSourceDesc {
+ method public long getLength();
+ method public long getOffset();
+ method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+ field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+ }
+
+ public static class FileDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase {
+ ctor public FileDataSourceDesc.Builder();
+ ctor public FileDataSourceDesc.Builder(android.media.FileDataSourceDesc);
+ method public android.media.FileDataSourceDesc build();
+ method public android.media.FileDataSourceDesc.Builder setDataSource(android.os.ParcelFileDescriptor);
+ method public android.media.FileDataSourceDesc.Builder setDataSource(android.os.ParcelFileDescriptor, long, long);
+ }
+
public abstract class Image implements java.lang.AutoCloseable {
method public abstract void close();
method public android.graphics.Rect getCropRect();
@@ -23859,6 +24002,7 @@
method public void releaseOutputBuffer(int, boolean);
method public void releaseOutputBuffer(int, long);
method public void reset();
+ method public void setAudioPresentation(android.media.AudioPresentation);
method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler);
method public void setCallback(android.media.MediaCodec.Callback);
method public void setInputSurface(android.view.Surface);
@@ -23881,6 +24025,7 @@
field public static final deprecated int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe
field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff
+ field public static final java.lang.String PARAMETER_KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
field public static final java.lang.String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
field public static final java.lang.String PARAMETER_KEY_SUSPEND = "drop-input-frames";
field public static final java.lang.String PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
@@ -24154,6 +24299,7 @@
field public static final int HEVCProfileMain = 1; // 0x1
field public static final int HEVCProfileMain10 = 2; // 0x2
field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
+ field public static final int HEVCProfileMain10HDR10Plus = 8192; // 0x2000
field public static final int HEVCProfileMainStill = 4; // 0x4
field public static final int MPEG2LevelH14 = 2; // 0x2
field public static final int MPEG2LevelHL = 3; // 0x3
@@ -24215,8 +24361,10 @@
field public static final int VP9Profile1 = 2; // 0x2
field public static final int VP9Profile2 = 4; // 0x4
field public static final int VP9Profile2HDR = 4096; // 0x1000
+ field public static final int VP9Profile2HDR10Plus = 16384; // 0x4000
field public static final int VP9Profile3 = 8; // 0x8
field public static final int VP9Profile3HDR = 8192; // 0x2000
+ field public static final int VP9Profile3HDR10Plus = 32768; // 0x8000
field public int level;
field public int profile;
}
@@ -24550,16 +24698,30 @@
public final class MediaFormat {
ctor public MediaFormat();
+ ctor public MediaFormat(android.media.MediaFormat);
+ method public boolean containsFeature(java.lang.String);
method public boolean containsKey(java.lang.String);
method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
method public java.nio.ByteBuffer getByteBuffer(java.lang.String);
+ method public java.nio.ByteBuffer getByteBuffer(java.lang.String, java.nio.ByteBuffer);
method public boolean getFeatureEnabled(java.lang.String);
+ method public java.util.Set<java.lang.String> getFeatures();
method public float getFloat(java.lang.String);
+ method public float getFloat(java.lang.String, float);
method public int getInteger(java.lang.String);
+ method public int getInteger(java.lang.String, int);
+ method public java.util.Set<java.lang.String> getKeys();
method public long getLong(java.lang.String);
+ method public long getLong(java.lang.String, long);
+ method public java.lang.Number getNumber(java.lang.String);
+ method public java.lang.Number getNumber(java.lang.String, java.lang.Number);
method public java.lang.String getString(java.lang.String);
+ method public java.lang.String getString(java.lang.String, java.lang.String);
+ method public int getValueTypeForKey(java.lang.String);
+ method public void removeFeature(java.lang.String);
+ method public void removeKey(java.lang.String);
method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
method public void setFeatureEnabled(java.lang.String, boolean);
method public void setFloat(java.lang.String, float);
@@ -24601,6 +24763,7 @@
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
field public static final java.lang.String KEY_GRID_COLUMNS = "grid-cols";
field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
+ field public static final java.lang.String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -24639,6 +24802,7 @@
field public static final java.lang.String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
field public static final java.lang.String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
field public static final java.lang.String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+ field public static final java.lang.String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
field public static final java.lang.String MIMETYPE_AUDIO_FLAC = "audio/flac";
field public static final java.lang.String MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
field public static final java.lang.String MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
@@ -24664,6 +24828,12 @@
field public static final java.lang.String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
field public static final java.lang.String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+ field public static final int TYPE_BYTE_BUFFER = 5; // 0x5
+ field public static final int TYPE_FLOAT = 3; // 0x3
+ field public static final int TYPE_INTEGER = 1; // 0x1
+ field public static final int TYPE_LONG = 2; // 0x2
+ field public static final int TYPE_NULL = 0; // 0x0
+ field public static final int TYPE_STRING = 4; // 0x4
}
public final class MediaMetadata implements android.os.Parcelable {
@@ -24823,6 +24993,7 @@
field public static final int MUXER_OUTPUT_3GPP = 2; // 0x2
field public static final int MUXER_OUTPUT_HEIF = 3; // 0x3
field public static final int MUXER_OUTPUT_MPEG_4 = 0; // 0x0
+ field public static final int MUXER_OUTPUT_OGG = 4; // 0x4
field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
}
@@ -25054,6 +25225,166 @@
field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
}
+ public class MediaPlayer2 implements android.media.AudioRouting java.lang.AutoCloseable {
+ ctor public MediaPlayer2(android.content.Context);
+ method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+ method public java.lang.Object attachAuxEffect(int);
+ method public boolean cancelCommand(java.lang.Object);
+ method public java.lang.Object clearNextDataSources();
+ method public void clearPendingCommands();
+ method public void close();
+ method public java.lang.Object deselectTrack(int);
+ method public android.media.AudioAttributes getAudioAttributes();
+ method public int getAudioSessionId();
+ method public long getBufferedPosition();
+ method public android.media.DataSourceDesc getCurrentDataSource();
+ method public long getCurrentPosition();
+ method public long getDuration();
+ method public float getMaxPlayerVolume();
+ method public android.os.PersistableBundle getMetrics();
+ method public android.media.PlaybackParams getPlaybackParams();
+ method public float getPlayerVolume();
+ method public android.media.AudioDeviceInfo getPreferredDevice();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
+ method public int getSelectedTrack(int);
+ method public int getState();
+ method public android.media.SyncParams getSyncParams();
+ method public android.media.MediaTimestamp getTimestamp();
+ method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
+ method public android.media.VideoSize getVideoSize();
+ method public boolean isLooping();
+ method public java.lang.Object loopCurrent(boolean);
+ method public java.lang.Object notifyWhenCommandLabelReached(java.lang.Object);
+ method public java.lang.Object pause();
+ method public java.lang.Object play();
+ method public java.lang.Object prepare();
+ method public void registerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.EventCallback);
+ method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
+ method public void reset();
+ method public java.lang.Object seekTo(long);
+ method public java.lang.Object seekTo(long, int);
+ method public java.lang.Object selectTrack(int);
+ method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
+ method public java.lang.Object setAudioSessionId(int);
+ method public java.lang.Object setAuxEffectSendLevel(float);
+ method public java.lang.Object setDataSource(android.media.DataSourceDesc);
+ method public java.lang.Object setDisplay(android.view.SurfaceHolder);
+ method public java.lang.Object setNextDataSource(android.media.DataSourceDesc);
+ method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>);
+ method public java.lang.Object setPlaybackParams(android.media.PlaybackParams);
+ method public java.lang.Object setPlayerVolume(float);
+ method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
+ method public java.lang.Object setScreenOnWhilePlaying(boolean);
+ method public java.lang.Object setSurface(android.view.Surface);
+ method public java.lang.Object setSyncParams(android.media.SyncParams);
+ method public java.lang.Object setWakeLock(android.os.PowerManager.WakeLock);
+ method public java.lang.Object skipToNext();
+ method public void unregisterEventCallback(android.media.MediaPlayer2.EventCallback);
+ field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1
+ field public static final int CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES = 30; // 0x1e
+ field public static final int CALL_COMPLETED_DESELECT_TRACK = 2; // 0x2
+ field public static final int CALL_COMPLETED_LOOP_CURRENT = 3; // 0x3
+ field public static final int CALL_COMPLETED_PAUSE = 4; // 0x4
+ field public static final int CALL_COMPLETED_PLAY = 5; // 0x5
+ field public static final int CALL_COMPLETED_PREPARE = 6; // 0x6
+ field public static final int CALL_COMPLETED_SEEK_TO = 14; // 0xe
+ field public static final int CALL_COMPLETED_SELECT_TRACK = 15; // 0xf
+ field public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16; // 0x10
+ field public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17; // 0x11
+ field public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12
+ field public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19; // 0x13
+ field public static final int CALL_COMPLETED_SET_DISPLAY = 33; // 0x21
+ field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22; // 0x16
+ field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23; // 0x17
+ field public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24; // 0x18
+ field public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26; // 0x1a
+ field public static final int CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING = 35; // 0x23
+ field public static final int CALL_COMPLETED_SET_SURFACE = 27; // 0x1b
+ field public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28; // 0x1c
+ field public static final int CALL_COMPLETED_SET_WAKE_LOCK = 34; // 0x22
+ field public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29; // 0x1d
+ field public static final int CALL_STATUS_BAD_VALUE = 2; // 0x2
+ field public static final int CALL_STATUS_ERROR_IO = 4; // 0x4
+ field public static final int CALL_STATUS_ERROR_UNKNOWN = -2147483648; // 0x80000000
+ field public static final int CALL_STATUS_INVALID_OPERATION = 1; // 0x1
+ field public static final int CALL_STATUS_NO_DRM_SCHEME = 6; // 0x6
+ field public static final int CALL_STATUS_NO_ERROR = 0; // 0x0
+ field public static final int CALL_STATUS_PERMISSION_DENIED = 3; // 0x3
+ field public static final int CALL_STATUS_SKIPPED = 5; // 0x5
+ field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14
+ field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11
+ field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8
+ field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
+ field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
+ field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+ field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
+ field public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4; // 0x4
+ field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
+ field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
+ field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
+ field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
+ field public static final int MEDIA_INFO_DATA_SOURCE_END = 5; // 0x5
+ field public static final int MEDIA_INFO_DATA_SOURCE_LIST_END = 6; // 0x6
+ field public static final int MEDIA_INFO_DATA_SOURCE_REPEAT = 7; // 0x7
+ field public static final int MEDIA_INFO_DATA_SOURCE_START = 2; // 0x2
+ field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
+ field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+ field public static final int MEDIA_INFO_PREPARED = 100; // 0x64
+ field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
+ field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
+ field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
+ field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
+ field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
+ field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
+ field public static final int PLAYER_STATE_ERROR = 1005; // 0x3ed
+ field public static final int PLAYER_STATE_IDLE = 1001; // 0x3e9
+ field public static final int PLAYER_STATE_PAUSED = 1003; // 0x3eb
+ field public static final int PLAYER_STATE_PLAYING = 1004; // 0x3ec
+ field public static final int PLAYER_STATE_PREPARED = 1002; // 0x3ea
+ field public static final int SEEK_CLOSEST = 3; // 0x3
+ field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
+ field public static final int SEEK_NEXT_SYNC = 1; // 0x1
+ field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
+ }
+
+ public static class MediaPlayer2.EventCallback {
+ ctor public MediaPlayer2.EventCallback();
+ method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+ method public void onCommandLabelReached(android.media.MediaPlayer2, java.lang.Object);
+ method public void onError(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+ method public void onInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+ method public void onMediaTimeDiscontinuity(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaTimestamp);
+ method public void onSubtitleData(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.SubtitleData);
+ method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.TimedMetaData);
+ method public void onVideoSizeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.VideoSize);
+ }
+
+ public static final class MediaPlayer2.MetricsConstants {
+ field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+ field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+ field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
+ field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
+ field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
+ field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
+ field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+ field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
+ field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
+ field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
+ field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
+ field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
+ }
+
+ public static class MediaPlayer2.TrackInfo {
+ method public android.media.MediaFormat getFormat();
+ method public java.lang.String getLanguage();
+ method public int getTrackType();
+ field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
+ field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
+ field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
+ field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
+ }
+
public class MediaRecorder implements android.media.AudioRouting {
ctor public MediaRecorder();
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -25118,6 +25449,7 @@
field public static final int AMR_WB = 2; // 0x2
field public static final int DEFAULT = 0; // 0x0
field public static final int HE_AAC = 4; // 0x4
+ field public static final int OPUS = 7; // 0x7
field public static final int VORBIS = 6; // 0x6
}
@@ -25168,6 +25500,7 @@
field public static final int DEFAULT = 0; // 0x0
field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
+ field public static final int OGG = 11; // 0xb
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
field public static final int WEBM = 9; // 0x9
@@ -25796,6 +26129,26 @@
ctor public UnsupportedSchemeException(java.lang.String);
}
+ public class UriDataSourceDesc extends android.media.DataSourceDesc {
+ method public android.content.Context getContext();
+ method public java.util.List<java.net.HttpCookie> getCookies();
+ method public java.util.Map<java.lang.String, java.lang.String> getHeaders();
+ method public android.net.Uri getUri();
+ }
+
+ public static class UriDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase {
+ ctor public UriDataSourceDesc.Builder();
+ ctor public UriDataSourceDesc.Builder(android.media.UriDataSourceDesc);
+ method public android.media.UriDataSourceDesc build();
+ method public android.media.UriDataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri);
+ method public android.media.UriDataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>, java.util.List<java.net.HttpCookie>);
+ }
+
+ public final class VideoSize {
+ method public int getHeight();
+ method public int getWidth();
+ }
+
public abstract interface VolumeAutomation {
method public abstract android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
}
@@ -26767,6 +27120,7 @@
public static final class MediaSessionManager.RemoteUserInfo {
ctor public MediaSessionManager.RemoteUserInfo(java.lang.String, int, int);
+ ctor public MediaSessionManager.RemoteUserInfo(java.lang.String, int, int, android.os.IBinder);
method public java.lang.String getPackageName();
method public int getPid();
method public int getUid();
@@ -28087,6 +28441,7 @@
method public int describeContents();
method public int getLinkDownstreamBandwidthKbps();
method public int getLinkUpstreamBandwidthKbps();
+ method public android.net.TransportInfo getTransportInfo();
method public boolean hasCapability(int);
method public boolean hasTransport(int);
method public void writeToParcel(android.os.Parcel, int);
@@ -28295,6 +28650,9 @@
field public static final int UNSUPPORTED = -1; // 0xffffffff
}
+ public abstract interface TransportInfo {
+ }
+
public abstract class Uri implements java.lang.Comparable android.os.Parcelable {
method public abstract android.net.Uri.Builder buildUpon();
method public int compareTo(android.net.Uri);
@@ -28330,6 +28688,7 @@
method public abstract boolean isRelative();
method public android.net.Uri normalizeScheme();
method public static android.net.Uri parse(java.lang.String);
+ method public java.lang.String toSafeString();
method public abstract java.lang.String toString();
method public static android.net.Uri withAppendedPath(android.net.Uri, java.lang.String);
method public static void writeToParcel(android.os.Parcel, android.net.Uri);
@@ -29117,10 +29476,11 @@
field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
- field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; // 0x2
- field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; // 0x3
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; // 0x3
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; // 0x4
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; // 0x2
field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
- field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; // 0x4
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; // 0x5
field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE";
field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
@@ -33538,6 +33898,7 @@
method public static boolean isExternalStorageRemovable();
method public static boolean isExternalStorageRemovable(java.io.File);
field public static java.lang.String DIRECTORY_ALARMS;
+ field public static java.lang.String DIRECTORY_AUDIOBOOKS;
field public static java.lang.String DIRECTORY_DCIM;
field public static java.lang.String DIRECTORY_DOCUMENTS;
field public static java.lang.String DIRECTORY_DOWNLOADS;
@@ -34047,6 +34408,7 @@
}
public final class PowerManager {
+ method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(java.lang.String);
@@ -34057,6 +34419,8 @@
method public boolean isWakeLockLevelSupported(int);
method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
method public void reboot(java.lang.String);
+ method public void registerThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback, java.util.concurrent.Executor);
+ method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback);
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
@@ -34071,6 +34435,18 @@
field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
+ field public static final int THERMAL_STATUS_CRITICAL = 4; // 0x4
+ field public static final int THERMAL_STATUS_EMERGENCY = 5; // 0x5
+ field public static final int THERMAL_STATUS_LIGHT = 1; // 0x1
+ field public static final int THERMAL_STATUS_MODERATE = 2; // 0x2
+ field public static final int THERMAL_STATUS_NONE = 0; // 0x0
+ field public static final int THERMAL_STATUS_SEVERE = 3; // 0x3
+ field public static final int THERMAL_STATUS_SHUTDOWN = 6; // 0x6
+ }
+
+ public static abstract class PowerManager.ThermalStatusCallback {
+ ctor public PowerManager.ThermalStatusCallback();
+ method public void onStatusChange(int);
}
public final class PowerManager.WakeLock {
@@ -34323,7 +34699,7 @@
method public static void endAsyncSection(java.lang.String, int);
method public static void endSection();
method public static boolean isEnabled();
- method public static void setCounter(java.lang.String, int);
+ method public static void setCounter(java.lang.String, long);
}
public class TransactionTooLargeException extends android.os.RemoteException {
@@ -34390,6 +34766,7 @@
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+ field public static final java.lang.String DISALLOW_CONTENT_CAPTURE = "no_content_capture";
field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
field public static final java.lang.String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
field public static final java.lang.String DISALLOW_DATA_ROAMING = "no_data_roaming";
@@ -34399,7 +34776,6 @@
field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY = "no_install_unknown_sources_globally";
- field public static final java.lang.String DISALLOW_INTELLIGENCE_CAPTURE = "no_intelligence_capture";
field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
@@ -35603,9 +35979,11 @@
}
public final class CalendarContract {
+ method public static boolean startViewCalendarEventInManagedProfile(android.content.Context, long, long, long, boolean, int);
field public static final java.lang.String ACCOUNT_TYPE_LOCAL = "LOCAL";
field public static final java.lang.String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
field public static final java.lang.String ACTION_HANDLE_CUSTOM_EVENT = "android.provider.calendar.action.HANDLE_CUSTOM_EVENT";
+ field public static final java.lang.String ACTION_VIEW_WORK_CALENDAR_EVENT = "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT";
field public static final java.lang.String AUTHORITY = "com.android.calendar";
field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
field public static final android.net.Uri CONTENT_URI;
@@ -35613,6 +35991,7 @@
field public static final java.lang.String EXTRA_EVENT_ALL_DAY = "allDay";
field public static final java.lang.String EXTRA_EVENT_BEGIN_TIME = "beginTime";
field public static final java.lang.String EXTRA_EVENT_END_TIME = "endTime";
+ field public static final java.lang.String EXTRA_EVENT_ID = "id";
}
public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns {
@@ -35729,6 +36108,7 @@
field public static final java.lang.String CALENDAR_LOCATION = "calendar_location";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DEFAULT_SORT_ORDER = "calendar_displayName";
+ field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
field public static final java.lang.String NAME = "name";
}
@@ -35757,6 +36137,7 @@
public static final class CalendarContract.Events implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.SyncColumns {
field public static final android.net.Uri CONTENT_EXCEPTION_URI;
field public static final android.net.Uri CONTENT_URI;
+ field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
}
protected static abstract interface CalendarContract.EventsColumns {
@@ -35848,6 +36229,10 @@
field public static final java.lang.String END = "end";
field public static final java.lang.String END_DAY = "endDay";
field public static final java.lang.String END_MINUTE = "endMinute";
+ field public static final android.net.Uri ENTERPRISE_CONTENT_BY_DAY_URI;
+ field public static final android.net.Uri ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI;
+ field public static final android.net.Uri ENTERPRISE_CONTENT_SEARCH_URI;
+ field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
field public static final java.lang.String EVENT_ID = "event_id";
field public static final java.lang.String START_DAY = "startDay";
field public static final java.lang.String START_MINUTE = "startMinute";
@@ -37093,22 +37478,26 @@
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
- method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
- method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
- method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
- method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
- method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
- method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri copyDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri createDocument(android.content.ContentInterface, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.content.IntentSender createWebLinkIntent(android.content.ContentInterface, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static boolean deleteDocument(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static void ejectRoot(android.content.ContentInterface, android.net.Uri);
+ method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
- method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public static android.os.Bundle getDocumentMetadata(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentInterface, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public static java.lang.String getRootId(android.net.Uri);
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
+ method public static boolean isChildDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+ method public static boolean isRootUri(android.content.Context, android.net.Uri);
+ method public static boolean isRootsUri(android.content.Context, android.net.Uri);
method public static boolean isTreeUri(android.net.Uri);
- method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
- method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
- method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.net.Uri moveDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean removeDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri renameDocument(android.content.ContentInterface, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
field public static final java.lang.String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -37117,7 +37506,16 @@
field public static final java.lang.String EXTRA_LOADING = "loading";
field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT";
+ field public static final java.lang.String METADATA_EXIF = "android:documentExif";
+ field public static final java.lang.String METADATA_TREE_COUNT = "android:metadataTreeCount";
+ field public static final java.lang.String METADATA_TREE_SIZE = "android:metadataTreeSize";
+ field public static final java.lang.String METADATA_TYPES = "android:documentMetadataTypes";
field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
+ field public static final java.lang.String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
+ field public static final java.lang.String QUERY_ARG_EXCLUDE_MEDIA = "android:query-arg-exclude-media";
+ field public static final java.lang.String QUERY_ARG_FILE_SIZE_OVER = "android:query-arg-file-size-over";
+ field public static final java.lang.String QUERY_ARG_LAST_MODIFIED_AFTER = "android:query-arg-last-modified-after";
+ field public static final java.lang.String QUERY_ARG_MIME_TYPES = "android:query-arg-mime-types";
}
public static final class DocumentsContract.Document {
@@ -37132,8 +37530,10 @@
field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
+ field public static final int FLAG_PARTIAL = 8192; // 0x2000
field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
+ field public static final int FLAG_SUPPORTS_METADATA = 16384; // 0x4000
field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
field public static final int FLAG_SUPPORTS_REMOVE = 1024; // 0x400
field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
@@ -37164,6 +37564,7 @@
field public static final java.lang.String COLUMN_ROOT_ID = "root_id";
field public static final java.lang.String COLUMN_SUMMARY = "summary";
field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final int FLAG_EMPTY = 64; // 0x40
field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
field public static final int FLAG_SUPPORTS_EJECT = 32; // 0x20
@@ -37182,6 +37583,7 @@
method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException;
method public void ejectRoot(java.lang.String);
method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public android.os.Bundle getDocumentMetadata(java.lang.String) throws java.io.FileNotFoundException;
method public java.lang.String[] getDocumentStreamTypes(java.lang.String, java.lang.String);
method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException;
method public final java.lang.String getType(android.net.Uri);
@@ -37206,6 +37608,7 @@
method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[], android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
method public android.database.Cursor querySearchDocuments(java.lang.String, java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
+ method public android.database.Cursor querySearchDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
method public void removeDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public java.lang.String renameDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public final void revokeDocumentPermission(java.lang.String);
@@ -37395,6 +37798,7 @@
field public static final java.lang.String COMPOSER = "composer";
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String IS_ALARM = "is_alarm";
+ field public static final java.lang.String IS_AUDIOBOOK = "is_audiobook";
field public static final java.lang.String IS_MUSIC = "is_music";
field public static final java.lang.String IS_NOTIFICATION = "is_notification";
field public static final java.lang.String IS_PODCAST = "is_podcast";
@@ -37481,6 +37885,7 @@
public static final class MediaStore.Downloads implements android.provider.MediaStore.DownloadColumns {
method public static android.net.Uri getContentUri(java.lang.String);
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/download";
field public static final android.net.Uri EXTERNAL_CONTENT_URI;
field public static final android.net.Uri INTERNAL_CONTENT_URI;
}
@@ -37513,8 +37918,8 @@
field public static final java.lang.String DATE_TAKEN = "datetaken";
field public static final java.lang.String DESCRIPTION = "description";
field public static final java.lang.String IS_PRIVATE = "isprivate";
- field public static final java.lang.String LATITUDE = "latitude";
- field public static final java.lang.String LONGITUDE = "longitude";
+ field public static final deprecated java.lang.String LATITUDE = "latitude";
+ field public static final deprecated java.lang.String LONGITUDE = "longitude";
field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
field public static final java.lang.String ORIENTATION = "orientation";
field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
@@ -37638,8 +38043,8 @@
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String IS_PRIVATE = "isprivate";
field public static final java.lang.String LANGUAGE = "language";
- field public static final java.lang.String LATITUDE = "latitude";
- field public static final java.lang.String LONGITUDE = "longitude";
+ field public static final deprecated java.lang.String LATITUDE = "latitude";
+ field public static final deprecated java.lang.String LONGITUDE = "longitude";
field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
field public static final java.lang.String RESOLUTION = "resolution";
field public static final java.lang.String TAGS = "tags";
@@ -38117,10 +38522,13 @@
}
public static final class Telephony.CarrierId implements android.provider.BaseColumns {
+ method public static android.net.Uri getPreciseCarrierIdUriForSubscriptionId(int);
method public static android.net.Uri getUriForSubscriptionId(int);
field public static final java.lang.String CARRIER_ID = "carrier_id";
field public static final java.lang.String CARRIER_NAME = "carrier_name";
field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String PRECISE_CARRIER_ID = "precise_carrier_id";
+ field public static final java.lang.String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
}
public static final class Telephony.Carriers implements android.provider.BaseColumns {
@@ -40124,6 +40532,7 @@
method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
+ method public android.service.autofill.FillResponse.Builder setUserData(android.service.autofill.UserData);
}
public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -40219,6 +40628,7 @@
public final class UserData implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String getFieldClassificationAlgorithm();
+ method public java.lang.String getFieldClassificationAlgorithmForCategory(java.lang.String);
method public java.lang.String getId();
method public static int getMaxCategoryCount();
method public static int getMaxFieldClassificationIdsSize();
@@ -40234,6 +40644,7 @@
method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
method public android.service.autofill.UserData build();
method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithm(java.lang.String, android.os.Bundle);
+ method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithmForCategory(java.lang.String, java.lang.String, android.os.Bundle);
}
public abstract interface Validator {
@@ -40263,13 +40674,16 @@
public class CarrierIdentifier implements android.os.Parcelable {
ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, int, int);
ctor public CarrierIdentifier(byte[], java.lang.String, java.lang.String);
method public int describeContents();
+ method public int getCarrierId();
method public java.lang.String getGid1();
method public java.lang.String getGid2();
method public java.lang.String getImsi();
method public java.lang.String getMcc();
method public java.lang.String getMnc();
+ method public int getPreciseCarrierId();
method public java.lang.String getSpn();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
@@ -41983,8 +42397,9 @@
method public void swapConference();
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
- field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+ field public static final deprecated java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
+ field public static final java.lang.String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_CONNECTING = 9; // 0x9
field public static final int STATE_DIALING = 1; // 0x1
@@ -42555,6 +42970,20 @@
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
}
+ public final class PhoneAccountSuggestion implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
+ method public int getReason();
+ method public boolean shouldAutoSelect();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountSuggestion> CREATOR;
+ field public static final int REASON_FREQUENT = 2; // 0x2
+ field public static final int REASON_INTRA_CARRIER = 1; // 0x1
+ field public static final int REASON_NONE = 0; // 0x0
+ field public static final int REASON_OTHER = 4; // 0x4
+ field public static final int REASON_USER_SET = 3; // 0x3
+ }
+
public final class RemoteConference {
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
@@ -42746,12 +43175,14 @@
field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
+ field public static final java.lang.String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
+ field public static final java.lang.String METADATA_IN_CALL_SERVICE_CAR_MODE_UI = "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
@@ -42900,6 +43331,19 @@
field public static final int BAND_9 = 9; // 0x9
}
+ public final class AvailableNetworkInfo implements android.os.Parcelable {
+ ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>);
+ method public int describeContents();
+ method public java.util.List<java.lang.String> getMccMncs();
+ method public int getPriority();
+ method public int getSubId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR;
+ field public static final int PRIORITY_HIGH = 1; // 0x1
+ field public static final int PRIORITY_LOW = 3; // 0x3
+ field public static final int PRIORITY_MED = 2; // 0x2
+ }
+
public class CarrierConfigManager {
method public android.os.PersistableBundle getConfig();
method public android.os.PersistableBundle getConfigForSubId(int);
@@ -42926,6 +43370,7 @@
field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final java.lang.String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
+ field public static final java.lang.String KEY_CARRIER_CONFIG_VERSION_STRING = "carrier_config_version_string";
field public static final java.lang.String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
field public static final java.lang.String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = "carrier_default_wfc_ims_mode_int";
field public static final java.lang.String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int";
@@ -43429,10 +43874,10 @@
method public static java.lang.String getStrippedReversed(java.lang.String);
method public static final boolean is12Key(char);
method public static final boolean isDialable(char);
- method public static boolean isEmergencyNumber(java.lang.String);
+ method public static deprecated boolean isEmergencyNumber(java.lang.String);
method public static boolean isGlobalPhoneNumber(java.lang.String);
method public static boolean isISODigit(char);
- method public static boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
+ method public static deprecated boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
method public static final boolean isNonSeparator(char);
method public static final boolean isReallyDialable(char);
method public static final boolean isStartsPostDial(char);
@@ -43723,12 +44168,14 @@
method public static int getSlotIndex(int);
method public int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
+ method public java.util.List<android.telephony.SubscriptionInfo> getSubscriptionsInGroup(int);
method public boolean isActiveSubscriptionId(int);
method public boolean isNetworkRoaming(int);
method public static boolean isUsableSubscriptionId(int);
method public static boolean isValidSubscriptionId(int);
method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+ method public boolean removeSubscriptionsFromGroup(int[]);
method public java.lang.String setSubscriptionGroup(int[]);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
@@ -43793,6 +44240,7 @@
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public android.os.PersistableBundle getCarrierConfig();
+ method public int getCarrierIdFromSimMccMnc();
method public deprecated android.telephony.CellLocation getCellLocation();
method public java.util.Map<java.lang.Integer, java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
method public java.util.Map<java.lang.Integer, java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
@@ -43830,6 +44278,8 @@
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
+ method public int getSimPreciseCarrierId();
+ method public java.lang.CharSequence getSimPreciseCarrierIdName();
method public java.lang.String getSimSerialNumber();
method public int getSimState();
method public int getSimState(int);
@@ -43878,6 +44328,7 @@
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
method public deprecated void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
+ method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>);
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
@@ -43885,6 +44336,7 @@
field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
field public static final java.lang.String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE";
field public static final java.lang.String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
+ field public static final java.lang.String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -43917,6 +44369,8 @@
field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
+ field public static final java.lang.String EXTRA_PRECISE_CARRIER_ID = "android.telephony.extra.PRECISE_CARRIER_ID";
+ field public static final java.lang.String EXTRA_PRECISE_CARRIER_NAME = "android.telephony.extra.PRECISE_CARRIER_NAME";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
@@ -44139,11 +44593,13 @@
method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
method public int getEmergencyServiceCategoryBitmask();
+ method public java.lang.String getMnc();
method public java.lang.String getNumber();
method public boolean isFromSources(int);
method public boolean isInEmergencyServiceCategories(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+ field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10
field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1
@@ -44877,6 +45333,7 @@
public static class PrecomputedText.Params.Builder {
ctor public PrecomputedText.Params.Builder(android.text.TextPaint);
+ ctor public PrecomputedText.Params.Builder(android.text.PrecomputedText.Params);
method public android.text.PrecomputedText.Params build();
method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int);
method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int);
@@ -45850,6 +46307,7 @@
method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
+ method public int getUnderlineColor();
method public void setFlags(int);
method public void updateDrawState(android.text.TextPaint);
method public void writeToParcel(android.os.Parcel, int);
@@ -47850,6 +48308,7 @@
method public final boolean isFunctionPressed();
method public static final boolean isGamepadButton(int);
method public final boolean isLongPress();
+ method public static final boolean isMediaSessionKey(int);
method public final boolean isMetaPressed();
method public static boolean isModifierKey(int);
method public final boolean isNumLockOn();
@@ -51564,6 +52023,21 @@
}
+package android.view.contentcapture {
+
+ public final class ContentCaptureManager {
+ method public android.content.ComponentName getServiceComponentName();
+ method public boolean isContentCaptureEnabled();
+ method public android.view.ViewStructure newVirtualViewStructure(android.view.autofill.AutofillId, int);
+ method public void notifyViewAppeared(android.view.ViewStructure);
+ method public void notifyViewDisappeared(android.view.autofill.AutofillId);
+ method public void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
+ method public void setContentCaptureEnabled(boolean);
+ field public static final int FLAG_USER_INPUT = 1; // 0x1
+ }
+
+}
+
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
@@ -51965,17 +52439,82 @@
}
-package android.view.intelligence {
+package android.view.inspector {
- public final class ContentCaptureManager {
- method public android.content.ComponentName getServiceComponentName();
- method public boolean isContentCaptureEnabled();
- method public android.view.ViewStructure newVirtualViewStructure(android.view.autofill.AutofillId, int);
- method public void notifyViewAppeared(android.view.ViewStructure);
- method public void notifyViewDisappeared(android.view.autofill.AutofillId);
- method public void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
- method public void setContentCaptureEnabled(boolean);
- field public static final int FLAG_USER_INPUT = 1; // 0x1
+ public abstract interface InspectionCompanion<T> {
+ method public default java.lang.String getNodeName();
+ method public abstract void mapProperties(android.view.inspector.PropertyMapper);
+ method public abstract void readProperties(T, android.view.inspector.PropertyReader);
+ }
+
+ public static class InspectionCompanion.UninitializedPropertyMapException extends java.lang.RuntimeException {
+ ctor public InspectionCompanion.UninitializedPropertyMapException();
+ }
+
+ public final class IntEnumMapping {
+ method public java.lang.String nameOf(int);
+ }
+
+ public static final class IntEnumMapping.Builder {
+ ctor public IntEnumMapping.Builder();
+ method public android.view.inspector.IntEnumMapping.Builder addValue(java.lang.String, int);
+ method public android.view.inspector.IntEnumMapping build();
+ method public void clear();
+ }
+
+ public final class IntFlagMapping {
+ method public java.lang.String[] namesOf(int);
+ }
+
+ public static final class IntFlagMapping.Builder {
+ ctor public IntFlagMapping.Builder();
+ method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int);
+ method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int, int);
+ method public android.view.inspector.IntFlagMapping build();
+ method public void clear();
+ }
+
+ public abstract interface PropertyMapper {
+ method public abstract int mapBoolean(java.lang.String, int);
+ method public abstract int mapByte(java.lang.String, int);
+ method public abstract int mapChar(java.lang.String, int);
+ method public abstract int mapColor(java.lang.String, int);
+ method public abstract int mapDouble(java.lang.String, int);
+ method public abstract int mapFloat(java.lang.String, int);
+ method public abstract int mapGravity(java.lang.String, int);
+ method public abstract int mapInt(java.lang.String, int);
+ method public abstract int mapIntEnum(java.lang.String, int, android.view.inspector.IntEnumMapping);
+ method public abstract int mapIntFlag(java.lang.String, int, android.view.inspector.IntFlagMapping);
+ method public abstract int mapLong(java.lang.String, int);
+ method public abstract int mapObject(java.lang.String, int);
+ method public abstract int mapShort(java.lang.String, int);
+ }
+
+ public static class PropertyMapper.PropertyConflictException extends java.lang.RuntimeException {
+ ctor public PropertyMapper.PropertyConflictException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public abstract interface PropertyReader {
+ method public abstract void readBoolean(int, boolean);
+ method public abstract void readByte(int, byte);
+ method public abstract void readChar(int, char);
+ method public abstract void readColor(int, int);
+ method public abstract void readColor(int, long);
+ method public abstract void readColor(int, android.graphics.Color);
+ method public abstract void readDouble(int, double);
+ method public abstract void readFloat(int, float);
+ method public abstract void readGravity(int, int);
+ method public abstract void readInt(int, int);
+ method public abstract void readIntEnum(int, int);
+ method public abstract void readIntFlag(int, int);
+ method public abstract void readLong(int, long);
+ method public abstract void readObject(int, java.lang.Object);
+ method public abstract void readShort(int, short);
+ }
+
+ public static class PropertyReader.PropertyTypeMismatchException extends java.lang.RuntimeException {
+ ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String, java.lang.String);
+ ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String);
}
}
@@ -52026,24 +52565,25 @@
method public int describeContents();
method public android.app.Person getAuthor();
method public android.os.Bundle getExtras();
+ method public java.time.ZonedDateTime getReferenceTime();
method public java.lang.CharSequence getText();
- method public java.time.ZonedDateTime getTime();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
field public static final android.app.Person PERSON_USER_LOCAL;
+ field public static final android.app.Person PERSON_USER_REMOTE;
}
public static final class ConversationActions.Message.Builder {
- ctor public ConversationActions.Message.Builder();
+ ctor public ConversationActions.Message.Builder(android.app.Person);
method public android.view.textclassifier.ConversationActions.Message build();
- method public android.view.textclassifier.ConversationActions.Message.Builder setAuthor(android.app.Person);
- method public android.view.textclassifier.ConversationActions.Message.Builder setComposeTime(java.time.ZonedDateTime);
method public android.view.textclassifier.ConversationActions.Message.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.ConversationActions.Message.Builder setReferenceTime(java.time.ZonedDateTime);
method public android.view.textclassifier.ConversationActions.Message.Builder setText(java.lang.CharSequence);
}
public static final class ConversationActions.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
method public java.util.List<java.lang.String> getHints();
method public int getMaxSuggestions();
@@ -52157,6 +52697,7 @@
public static final class TextClassification.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
method public android.os.Bundle getExtras();
@@ -52219,6 +52760,7 @@
method public default android.view.textclassifier.ConversationActions suggestConversationActions(android.view.textclassifier.ConversationActions.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(android.view.textclassifier.TextSelection.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
+ field public static final java.lang.String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
field public static final java.lang.String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
field public static final java.lang.String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable";
field public static final android.view.textclassifier.TextClassifier NO_OP;
@@ -52274,6 +52816,7 @@
public static final class TextLanguage.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.Bundle getExtras();
method public java.lang.CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
@@ -52305,6 +52848,7 @@
public static final class TextLinks.Builder {
ctor public TextLinks.Builder(java.lang.String);
method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>);
+ method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>, android.os.Bundle);
method public android.view.textclassifier.TextLinks build();
method public android.view.textclassifier.TextLinks.Builder clearTextLinks();
method public android.view.textclassifier.TextLinks.Builder setExtras(android.os.Bundle);
@@ -52312,6 +52856,7 @@
public static final class TextLinks.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
method public android.os.Bundle getExtras();
@@ -52334,6 +52879,7 @@
method public int getEnd();
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.os.Bundle getExtras();
method public int getStart();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.TextLink> CREATOR;
@@ -52368,6 +52914,7 @@
public static final class TextSelection.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
method public android.os.Bundle getExtras();
@@ -52868,6 +53415,7 @@
method public abstract boolean getDomStorageEnabled();
method public abstract java.lang.String getFantasyFontFamily();
method public abstract java.lang.String getFixedFontFamily();
+ method public int getForceDarkMode();
method public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
method public abstract boolean getJavaScriptEnabled();
method public abstract android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
@@ -52914,6 +53462,7 @@
method public abstract deprecated void setEnableSmoothTransition(boolean);
method public abstract void setFantasyFontFamily(java.lang.String);
method public abstract void setFixedFontFamily(java.lang.String);
+ method public void setForceDarkMode(int);
method public abstract deprecated void setGeolocationDatabasePath(java.lang.String);
method public abstract void setGeolocationEnabled(boolean);
method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
@@ -52944,6 +53493,9 @@
method public abstract void setUserAgentString(java.lang.String);
method public abstract boolean supportMultipleWindows();
method public abstract boolean supportZoom();
+ field public static final int FORCE_DARK_AUTO = 0; // 0x0
+ field public static final int FORCE_DARK_OFF = -1; // 0xffffffff
+ field public static final int FORCE_DARK_ON = 1; // 0x1
field public static final int LOAD_CACHE_ELSE_NETWORK = 1; // 0x1
field public static final int LOAD_CACHE_ONLY = 3; // 0x3
field public static final int LOAD_DEFAULT = -1; // 0xffffffff
@@ -54962,6 +55514,7 @@
method public void setInt(int, java.lang.String, int);
method public void setIntent(int, java.lang.String, android.content.Intent);
method public void setLabelFor(int, int);
+ method public void setLightBackgroundLayoutId(int);
method public void setLong(int, java.lang.String, long);
method public void setOnClickFillInIntent(int, android.content.Intent);
method public void setOnClickPendingIntent(int, android.app.PendingIntent);
@@ -55593,6 +56146,7 @@
method public java.lang.CharSequence getText();
method public android.view.textclassifier.TextClassifier getTextClassifier();
method public final android.content.res.ColorStateList getTextColors();
+ method public android.graphics.drawable.Drawable getTextCursorDrawable();
method public android.text.TextDirectionHeuristic getTextDirectionHeuristic();
method public java.util.Locale getTextLocale();
method public android.os.LocaleList getTextLocales();
@@ -55723,6 +56277,8 @@
method public void setTextClassifier(android.view.textclassifier.TextClassifier);
method public void setTextColor(int);
method public void setTextColor(android.content.res.ColorStateList);
+ method public void setTextCursorDrawable(android.graphics.drawable.Drawable);
+ method public void setTextCursorDrawable(int);
method public void setTextIsSelectable(boolean);
method public final void setTextKeepState(java.lang.CharSequence);
method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
diff --git a/api/removed.txt b/api/removed.txt
index f7106d2..e3e8b63 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -508,6 +508,21 @@
field public static final deprecated java.lang.String TIMESTAMP = "timestamp";
}
+ public final class DocumentsContract {
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
+ method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ }
+
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 7a2c233..da6840c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4,24 +4,23 @@
field public static final java.lang.String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
field public static final java.lang.String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final java.lang.String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
- field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final java.lang.String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field public static final deprecated java.lang.String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
+ field public static final java.lang.String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
field public static final java.lang.String ACCESS_MTP = "android.permission.ACCESS_MTP";
field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
field public static final java.lang.String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final java.lang.String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
- field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
field public static final java.lang.String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
field public static final java.lang.String BACKUP = "android.permission.BACKUP";
- field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
- field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
+ field public static final java.lang.String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
+ field public static final java.lang.String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
@@ -29,53 +28,40 @@
field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
- field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
- field public static final java.lang.String BIND_SMART_SUGGESTIONS_SERVICE = "android.permission.BIND_SMART_SUGGESTIONS_SERVICE";
field public static final java.lang.String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
- field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
field public static final java.lang.String BRICK = "android.permission.BRICK";
field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final deprecated java.lang.String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
- field public static final java.lang.String CALL_PRIVILEGED = "android.permission.CALL_PRIVILEGED";
field public static final java.lang.String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
field public static final java.lang.String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
- field public static final java.lang.String CAPTURE_AUDIO_OUTPUT = "android.permission.CAPTURE_AUDIO_OUTPUT";
field public static final java.lang.String CAPTURE_TV_INPUT = "android.permission.CAPTURE_TV_INPUT";
field public static final java.lang.String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
- field public static final java.lang.String CHANGE_COMPONENT_ENABLED_STATE = "android.permission.CHANGE_COMPONENT_ENABLED_STATE";
- field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
field public static final java.lang.String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST";
field public static final java.lang.String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+ field public static final java.lang.String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
field public static final java.lang.String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
- field public static final java.lang.String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
field public static final java.lang.String CONTROL_VPN = "android.permission.CONTROL_VPN";
field public static final java.lang.String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
- field public static final java.lang.String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
- field public static final java.lang.String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
field public static final java.lang.String DEVICE_POWER = "android.permission.DEVICE_POWER";
- field public static final java.lang.String DIAGNOSTIC = "android.permission.DIAGNOSTIC";
field public static final java.lang.String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
- field public static final java.lang.String DUMP = "android.permission.DUMP";
field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK";
field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
- field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
- field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
field public static final java.lang.String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS";
field public static final java.lang.String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
field public static final java.lang.String HARDWARE_TEST = "android.permission.HARDWARE_TEST";
@@ -83,18 +69,16 @@
field public static final java.lang.String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
field public static final java.lang.String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
- field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
- field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
+ field public static final java.lang.String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
field public static final java.lang.String INTERACT_ACROSS_USERS_FULL = "android.permission.INTERACT_ACROSS_USERS_FULL";
field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
field public static final java.lang.String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP";
field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
- field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final java.lang.String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
@@ -104,6 +88,7 @@
field public static final java.lang.String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL";
field public static final java.lang.String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
+ field public static final java.lang.String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
field public static final java.lang.String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
@@ -113,26 +98,21 @@
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final java.lang.String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
- field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
- field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
field public static final java.lang.String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
field public static final java.lang.String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
field public static final java.lang.String MODIFY_DAY_NIGHT_MODE = "android.permission.MODIFY_DAY_NIGHT_MODE";
field public static final deprecated java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
field public static final java.lang.String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
- field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
field public static final java.lang.String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
- field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
- field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
field public static final java.lang.String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
field public static final java.lang.String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
field public static final java.lang.String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
field public static final java.lang.String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final java.lang.String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final java.lang.String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
+ field public static final java.lang.String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
field public static final java.lang.String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
- field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
field public static final java.lang.String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
field public static final java.lang.String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
@@ -145,7 +125,6 @@
field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
- field public static final java.lang.String READ_LOGS = "android.permission.READ_LOGS";
field public static final java.lang.String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
field public static final java.lang.String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
@@ -157,7 +136,6 @@
field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
- field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
field public static final java.lang.String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
@@ -168,33 +146,26 @@
field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
+ field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
field public static final java.lang.String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
- field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
field public static final java.lang.String SERIAL_PORT = "android.permission.SERIAL_PORT";
field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
- field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
- field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
- field public static final java.lang.String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
field public static final java.lang.String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS";
field public static final java.lang.String SET_MEDIA_KEY_LISTENER = "android.permission.SET_MEDIA_KEY_LISTENER";
field public static final java.lang.String SET_ORIENTATION = "android.permission.SET_ORIENTATION";
field public static final java.lang.String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
- field public static final java.lang.String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
field public static final java.lang.String SET_SCREEN_COMPATIBILITY = "android.permission.SET_SCREEN_COMPATIBILITY";
- field public static final java.lang.String SET_TIME = "android.permission.SET_TIME";
field public static final java.lang.String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final java.lang.String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
field public static final java.lang.String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final java.lang.String SHUTDOWN = "android.permission.SHUTDOWN";
- field public static final java.lang.String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
- field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final java.lang.String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
field public static final java.lang.String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
@@ -203,18 +174,14 @@
field public static final java.lang.String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final java.lang.String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
field public static final java.lang.String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
- field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
field public static final java.lang.String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
- field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS";
field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
- field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final java.lang.String WRITE_OBB = "android.permission.WRITE_OBB";
- field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
}
public static final class Manifest.permission_group {
@@ -238,6 +205,7 @@
}
public static final class R.dimen {
+ field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008
field public static final int config_restrictedIconSize = 17104903; // 0x1050007
}
@@ -295,6 +263,7 @@
method public void killUid(int, java.lang.String);
method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method public static void setPersistentVrThread(int);
+ method public boolean switchUser(android.os.UserHandle);
}
public static abstract interface ActivityManager.OnUidImportanceListener {
@@ -311,7 +280,6 @@
method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
method public static java.lang.String[] getOpStrs();
method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
- method public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOpStrs(java.lang.String[]);
method public static int opToDefaultMode(java.lang.String);
method public static java.lang.String opToPermission(java.lang.String);
method public void setMode(java.lang.String, int, java.lang.String, int);
@@ -575,10 +543,6 @@
package android.app.admin {
- public class DeviceAdminReceiver extends android.content.BroadcastReceiver {
- method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
- }
-
public class DevicePolicyManager {
method public java.lang.String getDeviceOwner();
method public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -594,6 +558,8 @@
method public boolean isDeviceManaged();
method public boolean isDeviceProvisioned();
method public boolean isDeviceProvisioningConfigApplied();
+ method public boolean isManagedKiosk();
+ method public boolean isUnattendedManagedKiosk();
method public void notifyPendingSystemUpdate(long);
method public void notifyPendingSystemUpdate(long, boolean);
method public boolean packageHasActiveAdmins(java.lang.String);
@@ -611,7 +577,12 @@
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
field public static final java.lang.String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
field public static final java.lang.String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
+ field public static final java.lang.String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER";
field public static final java.lang.String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
+ field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
+ field public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
+ field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
+ field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
@@ -620,10 +591,7 @@
}
public final class SystemUpdatePolicy implements android.os.Parcelable {
- method public int describeContents();
method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
field public static final int TYPE_PAUSE = 4; // 0x4
}
@@ -842,6 +810,8 @@
method public int restoreAll(long, android.app.backup.RestoreObserver);
method public int restorePackage(java.lang.String, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor);
method public int restorePackage(java.lang.String, android.app.backup.RestoreObserver);
+ method public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, java.lang.String[]);
+ method public int restoreSome(long, android.app.backup.RestoreObserver, java.lang.String[]);
}
public class RestoreSet implements android.os.Parcelable {
@@ -873,12 +843,18 @@
package android.app.role {
+ public abstract interface OnRoleHoldersChangedListener {
+ method public abstract void onRoleHoldersChanged(java.lang.String, android.os.UserHandle);
+ }
+
public final class RoleManager {
+ method public void addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean addRoleHolderFromController(java.lang.String, java.lang.String);
method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
+ method public void removeOnRoleHoldersChangedListenerAsUser(android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
method public void setRoleNamesFromController(java.util.List<java.lang.String>);
@@ -924,6 +900,7 @@
}
public static final class UsageEvents.Event {
+ method public int getInstanceId();
method public java.lang.String getNotificationChannelId();
field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc
field public static final int NOTIFICATION_SEEN = 10; // 0xa
@@ -1028,6 +1005,14 @@
package android.content {
+ public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
+ method public void setDetectNotResponding(long);
+ }
+
+ public abstract class ContentResolver implements android.content.ContentInterface {
+ method public android.graphics.drawable.Drawable getTypeDrawable(java.lang.String);
+ }
+
public abstract class Context {
method public boolean bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
method public abstract android.content.Context createCredentialProtectedStorageContext();
@@ -1126,6 +1111,10 @@
field public int targetSandboxVersion;
}
+ public class CrossProfileApps {
+ method public void startAnyActivity(android.content.ComponentName, android.os.UserHandle);
+ }
+
public final class InstantAppInfo implements android.os.Parcelable {
ctor public InstantAppInfo(android.content.pm.ApplicationInfo, java.lang.String[], java.lang.String[]);
ctor public InstantAppInfo(java.lang.String, java.lang.CharSequence, java.lang.String[], java.lang.String[]);
@@ -1227,6 +1216,7 @@
method public abstract boolean arePermissionsIndividuallyControlled();
method public boolean canSuspendPackage(java.lang.String);
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
+ method public android.content.pm.ApplicationInfo getApplicationInfoAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.dex.ArtManager getArtManager();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public java.lang.CharSequence getHarmfulAppWarning(java.lang.String);
@@ -1242,6 +1232,9 @@
method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(android.content.Intent, int, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(android.content.Intent, int, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(android.content.Intent, int, android.os.UserHandle);
method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public deprecated void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
@@ -1338,8 +1331,10 @@
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
field public static final int FLAG_REMOVED = 2; // 0x2
+ field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
field public java.lang.String backgroundPermission;
field public int requestRes;
}
@@ -1384,7 +1379,7 @@
package android.content.pm.permission {
- public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
+ public final deprecated class RuntimePermissionPresentationInfo implements android.os.Parcelable {
ctor public RuntimePermissionPresentationInfo(java.lang.CharSequence, boolean, boolean);
method public int describeContents();
method public java.lang.CharSequence getLabel();
@@ -2917,6 +2912,13 @@
field public static final int RADIO_TUNER = 1998; // 0x7ce
}
+ public static class MediaTimestamp.Builder {
+ ctor public MediaTimestamp.Builder();
+ ctor public MediaTimestamp.Builder(android.media.MediaTimestamp);
+ method public android.media.MediaTimestamp build();
+ method public android.media.MediaTimestamp.Builder setMediaTimestamp(long, long, float);
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
@@ -2926,6 +2928,20 @@
method public void stop();
}
+ public static class SubtitleData.Builder {
+ ctor public SubtitleData.Builder();
+ ctor public SubtitleData.Builder(android.media.SubtitleData);
+ method public android.media.SubtitleData build();
+ method public android.media.SubtitleData.Builder setSubtitleData(int, long, long, byte[]);
+ }
+
+ public static class TimedMetaData.Builder {
+ ctor public TimedMetaData.Builder();
+ ctor public TimedMetaData.Builder(android.media.TimedMetaData);
+ method public android.media.TimedMetaData build();
+ method public android.media.TimedMetaData.Builder setTimedMetaData(long, byte[]);
+ }
+
}
package android.media.audiopolicy {
@@ -3193,7 +3209,6 @@
method public void addBlockedRating(android.media.tv.TvContentRating);
method public boolean captureFrame(java.lang.String, android.view.Surface, android.media.tv.TvStreamConfig);
method public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(java.lang.String);
- method public java.util.List<android.media.tv.TvContentRating> getBlockedRatings();
method public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
method public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
method public boolean isSingleSessionActive();
@@ -3439,11 +3454,9 @@
}
public class TrafficStats {
- method public static void clearThreadStatsUid();
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
method public static void setThreadStatsTagRestore();
- method public static void setThreadStatsUid(int);
}
public class VpnService extends android.app.Service {
@@ -3465,14 +3478,6 @@
}
-package android.net.http {
-
- public class X509TrustManagerExtensions {
- method public boolean isSameTrustConfiguration(java.lang.String, java.lang.String);
- }
-
-}
-
package android.net.wifi {
public deprecated class RttManager {
@@ -3696,7 +3701,6 @@
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method public int getWifiApState();
- method public boolean isDeviceToApRttSupported();
method public boolean isDeviceToDeviceRttSupported();
method public boolean isPortableHotspotSupported();
method public boolean isWifiApEnabled();
@@ -3889,14 +3893,6 @@
}
-package android.net.wifi.p2p {
-
- public class WifiP2pManager {
- method public void factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
- }
-
-}
-
package android.net.wifi.rtt {
public static final class RangingRequest.Builder {
@@ -3975,6 +3971,19 @@
field public static final java.lang.String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP";
}
+ public class Binder implements android.os.IBinder {
+ method public static final long clearCallingWorkSource();
+ method public static final int getCallingWorkSourceUid();
+ method public static final void restoreCallingWorkSource(long);
+ method public static final long setCallingWorkSourceUid(int);
+ method public static void setProxyTransactListener(android.os.Binder.ProxyTransactListener);
+ }
+
+ public static abstract interface Binder.ProxyTransactListener {
+ method public abstract void onTransactEnded(java.lang.Object);
+ method public abstract java.lang.Object onTransactStarted(android.os.IBinder, int);
+ }
+
public final class ConfigUpdate {
field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB";
field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
@@ -4308,6 +4317,7 @@
}
public class UserManager {
+ method public boolean canSwitchUsers();
method public void clearSeedAccountData();
method public android.os.UserHandle getProfileParent(android.os.UserHandle);
method public java.lang.String getSeedAccountName();
@@ -4323,6 +4333,7 @@
method public boolean isManagedProfile(int);
method public boolean isPrimaryUser();
method public boolean isRestrictedProfile();
+ method public boolean removeUser(android.os.UserHandle);
field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field public static final deprecated java.lang.String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final java.lang.String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -4382,11 +4393,31 @@
method public int getTargetSdk();
}
+ public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
+ ctor public RuntimePermissionPresentationInfo(java.lang.CharSequence, boolean, boolean);
+ method public int describeContents();
+ method public java.lang.CharSequence getLabel();
+ method public boolean isGranted();
+ method public boolean isStandard();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
+ }
+
+ public abstract class RuntimePermissionPresenterService extends android.app.Service {
+ ctor public RuntimePermissionPresenterService();
+ method public final void attachBaseContext(android.content.Context);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
+ method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+ method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.permission.RuntimePermissionPresenterService";
+ }
+
}
package android.permissionpresenterservice {
- public abstract class RuntimePermissionPresenterService extends android.app.Service {
+ public abstract deprecated class RuntimePermissionPresenterService extends android.app.Service {
ctor public RuntimePermissionPresenterService();
method public final void attachBaseContext(android.content.Context);
method public final android.os.IBinder onBind(android.content.Intent);
@@ -4497,6 +4528,21 @@
field public static final java.lang.String STATE = "state";
}
+ public final class DocumentsContract {
+ method public static boolean isManageMode(android.net.Uri);
+ method public static android.net.Uri setManageMode(android.net.Uri);
+ field public static final java.lang.String ACTION_DOCUMENT_ROOT_SETTINGS = "android.provider.action.DOCUMENT_ROOT_SETTINGS";
+ field public static final java.lang.String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
+ field public static final java.lang.String EXTRA_SHOW_ADVANCED = "android.provider.extra.SHOW_ADVANCED";
+ }
+
+ public static final class DocumentsContract.Root {
+ field public static final int FLAG_ADVANCED = 65536; // 0x10000
+ field public static final int FLAG_HAS_SETTINGS = 131072; // 0x20000
+ field public static final int FLAG_REMOVABLE_SD = 262144; // 0x40000
+ field public static final int FLAG_REMOVABLE_USB = 524288; // 0x80000
+ }
+
public abstract class SearchIndexableData {
ctor public SearchIndexableData();
ctor public SearchIndexableData(android.content.Context);
@@ -4629,6 +4675,7 @@
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+ field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled";
field public static final java.lang.String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -4658,18 +4705,18 @@
public static final class Telephony.Carriers implements android.provider.BaseColumns {
field public static final java.lang.String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
- field public static final java.lang.String EDITED = "edited";
- field public static final java.lang.String MAX_CONNS = "max_conns";
- field public static final java.lang.String MAX_CONNS_TIME = "max_conns_time";
- field public static final java.lang.String MODEM_COGNITIVE = "modem_cognitive";
+ field public static final java.lang.String EDITED_STATUS = "edited";
+ field public static final java.lang.String MAX_CONNECTIONS = "max_conns";
+ field public static final java.lang.String MODEM_PERSIST = "modem_cognitive";
field public static final java.lang.String MTU = "mtu";
- field public static final int NO_SET_SET = 0; // 0x0
+ field public static final int NO_APN_SET_ID = 0; // 0x0
+ field public static final java.lang.String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
field public static final int UNEDITED = 0; // 0x0
field public static final int USER_DELETED = 2; // 0x2
field public static final java.lang.String USER_EDITABLE = "user_editable";
field public static final int USER_EDITED = 1; // 0x1
field public static final java.lang.String USER_VISIBLE = "user_visible";
- field public static final java.lang.String WAIT_TIME = "wait_time";
+ field public static final java.lang.String WAIT_TIME_RETRY = "wait_time";
}
public final class TimeZoneRulesDataContract {
@@ -4840,7 +4887,10 @@
public abstract class AutofillFieldClassificationService extends android.app.Service {
method public android.os.IBinder onBind(android.content.Intent);
- method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+ method public float[][] onCalculateScores(java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>, java.util.List<java.lang.String>, java.lang.String, android.os.Bundle, java.util.Map, java.util.Map);
+ method public deprecated float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+ field public static final java.lang.String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
+ field public static final java.lang.String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
@@ -4848,6 +4898,69 @@
}
+package android.service.autofill.augmented {
+
+ public abstract class AugmentedAutofillService extends android.app.Service {
+ ctor public AugmentedAutofillService();
+ method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
+ }
+
+ public final class FillCallback {
+ method public void onSuccess(android.service.autofill.augmented.FillResponse);
+ }
+
+ public final class FillController {
+ method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+ }
+
+ public final class FillRequest {
+ method public android.content.ComponentName getActivityComponent();
+ method public android.view.autofill.AutofillId getFocusedId();
+ method public android.view.autofill.AutofillValue getFocusedValue();
+ method public android.service.autofill.augmented.PresentationParams getPresentationParams();
+ method public int getTaskId();
+ }
+
+ public final class FillResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.augmented.FillResponse> CREATOR;
+ }
+
+ public static final class FillResponse.Builder {
+ ctor public FillResponse.Builder();
+ method public android.service.autofill.augmented.FillResponse build();
+ method public android.service.autofill.augmented.FillResponse.Builder setFillWindow(android.service.autofill.augmented.FillWindow);
+ method public android.service.autofill.augmented.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+ }
+
+ public final class FillWindow implements java.lang.AutoCloseable {
+ ctor public FillWindow();
+ method public void destroy();
+ method public boolean update(android.service.autofill.augmented.PresentationParams.Area, android.view.View, long);
+ field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+ }
+
+ public abstract class PresentationParams {
+ method public int getFlags();
+ method public android.service.autofill.augmented.PresentationParams.Area getFullArea();
+ method public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
+ field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+ field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+ field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+ field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+ field public static final int FLAG_HOST_IME = 16; // 0x10
+ field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+ }
+
+ public static abstract class PresentationParams.Area {
+ method public android.graphics.Rect getBounds();
+ method public android.service.autofill.augmented.PresentationParams.Area getSubArea(android.graphics.Rect);
+ }
+
+}
+
package android.service.carrier {
public abstract class ApnService extends android.app.Service {
@@ -4858,6 +4971,58 @@
}
+package android.service.contentcapture {
+
+ public final class ContentCaptureEventsRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.contentcapture.ContentCaptureEventsRequest> CREATOR;
+ }
+
+ public abstract class ContentCaptureService extends android.app.Service {
+ ctor public ContentCaptureService();
+ method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
+ method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
+ method public void onActivitySnapshot(android.service.contentcapture.InteractionSessionId, android.service.contentcapture.SnapshotData);
+ method public abstract void onContentCaptureEventsRequest(android.service.contentcapture.InteractionSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
+ method public void onCreateInteractionSession(android.service.contentcapture.InteractionContext, android.service.contentcapture.InteractionSessionId);
+ method public void onDestroyInteractionSession(android.service.contentcapture.InteractionSessionId);
+ method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
+ method public final void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
+ method public final void setPackageContentCaptureEnabled(java.lang.String, boolean);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
+ }
+
+ public final class InteractionContext implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getActivityComponent();
+ method public int getDisplayId();
+ method public int getFlags();
+ method public int getTaskId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.contentcapture.InteractionContext> CREATOR;
+ field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
+ field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
+ }
+
+ public final class InteractionSessionId implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.contentcapture.InteractionSessionId> CREATOR;
+ }
+
+ public final class SnapshotData implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.app.assist.AssistContent getAssistContent();
+ method public android.os.Bundle getAssistData();
+ method public android.app.assist.AssistStructure getAssistStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.contentcapture.SnapshotData> CREATOR;
+ }
+
+}
+
package android.service.euicc {
public final class EuiccProfileInfo implements android.os.Parcelable {
@@ -4978,110 +5143,6 @@
}
-package android.service.intelligence {
-
- public final class ContentCaptureEventsRequest implements android.os.Parcelable {
- method public int describeContents();
- method public java.util.List<android.view.intelligence.ContentCaptureEvent> getEvents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.intelligence.ContentCaptureEventsRequest> CREATOR;
- }
-
- public final class FillCallback {
- method public void onSuccess(android.service.intelligence.FillResponse);
- }
-
- public final class FillController {
- method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
- }
-
- public final class FillRequest {
- method public android.view.autofill.AutofillId getFocusedId();
- method public android.service.intelligence.PresentationParams getPresentationParams();
- method public android.service.intelligence.InteractionSessionId getSessionId();
- }
-
- public final class FillResponse implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.intelligence.FillResponse> CREATOR;
- }
-
- public static class FillResponse.Builder {
- ctor public FillResponse.Builder();
- method public android.service.intelligence.FillResponse build();
- method public android.service.intelligence.FillResponse.Builder setFillWindow(android.service.intelligence.FillWindow);
- method public android.service.intelligence.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
- }
-
- public final class FillWindow {
- ctor public FillWindow();
- method public void destroy();
- method public boolean update(android.service.intelligence.PresentationParams.Area, android.view.View, long);
- field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
- }
-
- public final class InteractionContext implements android.os.Parcelable {
- method public int describeContents();
- method public android.content.ComponentName getActivityComponent();
- method public int getDisplayId();
- method public int getFlags();
- method public int getTaskId();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionContext> CREATOR;
- field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
- field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
- }
-
- public final class InteractionSessionId implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR;
- }
-
- public abstract class PresentationParams {
- method public int getFlags();
- method public android.service.intelligence.PresentationParams.Area getFullArea();
- method public android.service.intelligence.PresentationParams.Area getSuggestionArea();
- field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
- field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
- field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
- field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
- field public static final int FLAG_HOST_IME = 16; // 0x10
- field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
- }
-
- public static abstract class PresentationParams.Area {
- method public android.graphics.Rect getBounds();
- method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect);
- }
-
- public abstract class SmartSuggestionsService extends android.app.Service {
- ctor public SmartSuggestionsService();
- method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
- method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
- method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
- method public abstract void onContentCaptureEventsRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.ContentCaptureEventsRequest);
- method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
- method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
- method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
- method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
- method public final void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
- method public final void setPackageContentCaptureEnabled(java.lang.String, boolean);
- field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.SmartSuggestionsService";
- }
-
- public final class SnapshotData implements android.os.Parcelable {
- method public int describeContents();
- method public android.app.assist.AssistContent getAssistContent();
- method public android.os.Bundle getAssistData();
- method public android.app.assist.AssistStructure getAssistStructure();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.intelligence.SnapshotData> CREATOR;
- }
-
-}
-
package android.service.notification {
public final class Adjustment implements android.os.Parcelable {
@@ -5103,28 +5164,6 @@
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
- public final class Condition implements android.os.Parcelable {
- ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
- method public android.service.notification.Condition copy();
- method public static boolean isValidId(android.net.Uri, java.lang.String);
- method public static android.net.Uri.Builder newId(android.content.Context);
- method public static java.lang.String relevanceToString(int);
- method public static java.lang.String stateToString(int);
- field public static final int FLAG_RELEVANT_ALWAYS = 2; // 0x2
- field public static final int FLAG_RELEVANT_NOW = 1; // 0x1
- field public static final java.lang.String SCHEME = "condition";
- field public static final int STATE_ERROR = 3; // 0x3
- field public static final int STATE_UNKNOWN = 2; // 0x2
- field public final int flags;
- field public final int icon;
- field public final java.lang.String line1;
- field public final java.lang.String line2;
- }
-
- public abstract class ConditionProviderService extends android.app.Service {
- method public void onRequestConditions(int);
- }
-
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
@@ -5547,6 +5586,10 @@
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
}
+ public final class PhoneAccountSuggestion implements android.os.Parcelable {
+ ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
+ }
+
public final class RemoteConference {
method public deprecated void setAudioState(android.telecom.AudioState);
}
@@ -5603,7 +5646,6 @@
method public void clearPhoneAccounts();
method public android.telecom.TelecomAnalytics dumpAnalytics();
method public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean);
- method public boolean endCall();
method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
method public int getAllPhoneAccountsCount();
@@ -5615,7 +5657,6 @@
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
method public boolean isInEmergencyCall();
method public boolean isRinging();
- method public boolean isTtySupported();
method public boolean setDefaultDialer(java.lang.String);
field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final java.lang.String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
@@ -5640,7 +5681,6 @@
method public void overrideConfig(int, android.os.PersistableBundle);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
- field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
}
public class MbmsDownloadSession implements java.lang.AutoCloseable {
@@ -5711,6 +5751,26 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public abstract interface NumberVerificationCallback {
+ method public default void onCallReceived(java.lang.String);
+ method public default void onVerificationFailed(int);
+ field public static final int REASON_CONCURRENT_REQUESTS = 4; // 0x4
+ field public static final int REASON_IN_ECBM = 5; // 0x5
+ field public static final int REASON_IN_EMERGENCY_CALL = 6; // 0x6
+ field public static final int REASON_NETWORK_NOT_AVAILABLE = 2; // 0x2
+ field public static final int REASON_TIMED_OUT = 1; // 0x1
+ field public static final int REASON_TOO_MANY_CALLS = 3; // 0x3
+ field public static final int REASON_UNSPECIFIED = 0; // 0x0
+ }
+
+ public final class PhoneNumberRange implements android.os.Parcelable {
+ ctor public PhoneNumberRange(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method public int describeContents();
+ method public boolean matches(java.lang.String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.PhoneNumberRange> CREATOR;
+ }
+
public class PhoneStateListener {
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
@@ -5735,7 +5795,6 @@
public final class SmsManager {
method public void sendMultipartTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
- method public void sendTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
field public static final int RESULT_CANCELLED = 23; // 0x17
field public static final int RESULT_ENCODING_ERROR = 18; // 0x12
field public static final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; // 0x6
@@ -5762,13 +5821,9 @@
public class SubscriptionManager {
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
- method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
- method public void setSubscriptionOverrideCongested(int, boolean, long);
- method public void setSubscriptionOverrideUnmetered(int, boolean, long);
- method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
- field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
- field public static final java.lang.String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
+ method public void setDefaultDataSubId(int);
+ method public void setDefaultSmsSubId(int);
field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
field public static final android.net.Uri VT_ENABLED_CONTENT_URI;
field public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
@@ -5777,37 +5832,10 @@
field public static final android.net.Uri WFC_ROAMING_MODE_CONTENT_URI;
}
- public final class SubscriptionPlan implements android.os.Parcelable {
- method public java.util.Iterator<android.util.Range<java.time.ZonedDateTime>> cycleIterator();
- method public int describeContents();
- method public int getDataLimitBehavior();
- method public long getDataLimitBytes();
- method public long getDataUsageBytes();
- method public long getDataUsageTime();
- method public java.lang.CharSequence getSummary();
- method public java.lang.CharSequence getTitle();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final long BYTES_UNKNOWN = -1L; // 0xffffffffffffffffL
- field public static final long BYTES_UNLIMITED = 9223372036854775807L; // 0x7fffffffffffffffL
- field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionPlan> CREATOR;
- field public static final int LIMIT_BEHAVIOR_BILLED = 1; // 0x1
- field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
- field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
- field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
- field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
- }
-
public static class SubscriptionPlan.Builder {
- method public android.telephony.SubscriptionPlan build();
- method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
- method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
- method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
- method public android.telephony.SubscriptionPlan.Builder setSummary(java.lang.CharSequence);
- method public android.telephony.SubscriptionPlan.Builder setTitle(java.lang.CharSequence);
}
public final class TelephonyHistogram implements android.os.Parcelable {
@@ -5840,6 +5868,7 @@
method public void enableVideoCalling(boolean);
method public java.lang.String getAidForAppType(int);
method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
+ method public int getCardIdForDefaultEuicc();
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
method public java.lang.String getCdmaMdn();
@@ -5865,6 +5894,7 @@
method public int getVoiceActivationState();
method public boolean handlePinMmi(java.lang.String);
method public boolean handlePinMmiForSubscriber(int, java.lang.String);
+ method public boolean isCurrentPotentialEmergencyNumber(java.lang.String);
method public boolean isDataConnectivityPossible();
method public deprecated boolean isIdle();
method public deprecated boolean isOffhook();
@@ -5875,6 +5905,7 @@
method public boolean needsOtaServiceProvisioning();
method public boolean rebootRadio();
method public void requestCellInfoUpdate(android.os.WorkSource, java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback);
+ method public void requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback);
method public boolean resetRadioConfig();
method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method public void setCarrierDataEnabled(boolean);
@@ -5904,6 +5935,8 @@
field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+ field public static final int INVALID_CARD_ID = -1; // 0xffffffff
+ field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
@@ -6724,12 +6757,6 @@
method public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int);
}
- public static abstract class ImsSsInfo.ClirInterrogationStatus implements java.lang.annotation.Annotation {
- }
-
- public static abstract class ImsSsInfo.ClirOutgoingState implements java.lang.annotation.Annotation {
- }
-
public final class ImsStreamMediaProfile implements android.os.Parcelable {
ctor public ImsStreamMediaProfile(int, int, int, int, int);
method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
@@ -7229,12 +7256,13 @@
package android.view.accessibility {
public final class AccessibilityManager {
+ method public int getAccessibilityWindowId(android.os.IBinder);
method public void performAccessibilityShortcut();
}
}
-package android.view.intelligence {
+package android.view.contentcapture {
public final class ContentCaptureEvent implements android.os.Parcelable {
method public int describeContents();
@@ -7243,13 +7271,13 @@
method public android.view.autofill.AutofillId getId();
method public java.lang.CharSequence getText();
method public int getType();
- method public android.view.intelligence.ViewNode getViewNode();
+ method public android.view.contentcapture.ViewNode getViewNode();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.view.intelligence.ContentCaptureEvent> CREATOR;
- field public static final int TYPE_ACTIVITY_PAUSED = 3; // 0x3
- field public static final int TYPE_ACTIVITY_RESUMED = 2; // 0x2
- field public static final int TYPE_ACTIVITY_STARTED = 1; // 0x1
- field public static final int TYPE_ACTIVITY_STOPPED = 4; // 0x4
+ field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+ field public static final deprecated int TYPE_ACTIVITY_PAUSED = 3; // 0x3
+ field public static final deprecated int TYPE_ACTIVITY_RESUMED = 2; // 0x2
+ field public static final deprecated int TYPE_ACTIVITY_STARTED = 1; // 0x1
+ field public static final deprecated int TYPE_ACTIVITY_STOPPED = 4; // 0x4
field public static final int TYPE_VIEW_APPEARED = 5; // 0x5
field public static final int TYPE_VIEW_DISAPPEARED = 6; // 0x6
field public static final int TYPE_VIEW_TEXT_CHANGED = 7; // 0x7
@@ -7336,22 +7364,8 @@
ctor public SslErrorHandler();
}
- public abstract class TokenBindingService {
+ public abstract deprecated class TokenBindingService {
ctor public TokenBindingService();
- method public abstract void deleteAllKeys(android.webkit.ValueCallback<java.lang.Boolean>);
- method public abstract void deleteKey(android.net.Uri, android.webkit.ValueCallback<java.lang.Boolean>);
- method public abstract void enableTokenBinding();
- method public static android.webkit.TokenBindingService getInstance();
- method public abstract void getKey(android.net.Uri, java.lang.String[], android.webkit.ValueCallback<android.webkit.TokenBindingService.TokenBindingKey>);
- field public static final java.lang.String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256";
- field public static final java.lang.String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5";
- field public static final java.lang.String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS";
- }
-
- public static abstract class TokenBindingService.TokenBindingKey {
- ctor public TokenBindingService.TokenBindingKey();
- method public abstract java.lang.String getAlgorithm();
- method public abstract java.security.KeyPair getKeyPair();
}
public class WebChromeClient {
@@ -7481,7 +7495,7 @@
method public abstract android.webkit.GeolocationPermissions getGeolocationPermissions();
method public abstract android.webkit.ServiceWorkerController getServiceWorkerController();
method public abstract android.webkit.WebViewFactoryProvider.Statics getStatics();
- method public abstract android.webkit.TokenBindingService getTokenBindingService();
+ method public abstract deprecated android.webkit.TokenBindingService getTokenBindingService();
method public abstract android.webkit.TracingController getTracingController();
method public abstract android.webkit.WebIconDatabase getWebIconDatabase();
method public abstract android.webkit.WebStorage getWebStorage();
diff --git a/api/test-current.txt b/api/test-current.txt
index 46cbb52..627ef22 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5,11 +5,12 @@
field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final java.lang.String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
- field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
+ field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final java.lang.String WRITE_OBB = "android.permission.WRITE_OBB";
}
}
@@ -276,6 +277,23 @@
}
+package android.app.role {
+
+ public final class RoleManager {
+ method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+ method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+ method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
+ method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
+ method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+ }
+
+ public abstract interface RoleManagerCallback {
+ method public abstract void onFailure();
+ method public abstract void onSuccess();
+ }
+
+}
+
package android.app.usage {
public class NetworkStatsManager {
@@ -299,7 +317,11 @@
package android.content {
- public abstract class ContentResolver {
+ public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
+ method public void setDetectNotResponding(long);
+ }
+
+ public abstract class ContentResolver implements android.content.ContentInterface {
method public static java.lang.String[] getSyncAdapterPackagesForAuthorityAsUser(java.lang.String, int);
}
@@ -340,6 +362,7 @@
method public abstract java.lang.String getPermissionControllerPackageName();
method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
method public abstract java.lang.String getSharedSystemSharedLibraryPackageName();
+ method public java.lang.String getWellbeingPackageName();
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
@@ -351,8 +374,10 @@
}
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+ field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
field public java.lang.String backgroundPermission;
}
@@ -983,6 +1008,11 @@
field public static final android.net.Uri CORP_CONTENT_URI;
}
+ public final class MediaStore {
+ method public static void deleteContributedMedia(android.content.Context, java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ method public static long getContributedMediaSize(android.content.Context, java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ }
+
public final class Settings {
field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
}
@@ -1066,6 +1096,15 @@
package android.service.autofill {
+ public abstract class AutofillFieldClassificationService extends android.app.Service {
+ method public android.os.IBinder onBind(android.content.Intent);
+ field public static final java.lang.String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
+ field public static final java.lang.String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
+ field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
+ field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
+ }
+
public final class CharSequenceTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
}
@@ -1122,6 +1161,10 @@
method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
}
+ public final class UserData implements android.os.Parcelable {
+ method public android.util.ArrayMap<java.lang.String, java.lang.String> getFieldClassificationAlgorithms();
+ }
+
public abstract interface ValueFinder {
method public default java.lang.String findByAutofillId(android.view.autofill.AutofillId);
method public abstract android.view.autofill.AutofillValue findRawValueByAutofillId(android.view.autofill.AutofillId);
@@ -1240,6 +1283,10 @@
ctor public CallAudioState(boolean, int, int, android.bluetooth.BluetoothDevice, java.util.Collection<android.bluetooth.BluetoothDevice>);
}
+ public final class PhoneAccountSuggestion implements android.os.Parcelable {
+ ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
+ }
+
}
package android.telephony {
@@ -1663,11 +1710,6 @@
method public static int getLongPressTooltipHideTimeout();
}
- public final class ViewTreeObserver {
- method public void registerFrameCommitCallback(java.lang.Runnable);
- method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
- }
-
public abstract interface WindowManager implements android.view.ViewManager {
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 3723fce..e3748f1 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -201,7 +201,7 @@
private void doEnabled(@UserIdInt int userId) {
try {
- boolean isEnabled = mBmgr.isBackupEnabled();
+ boolean isEnabled = mBmgr.isBackupEnabledForUser(userId);
System.out.println("Backup Manager currently "
+ enableToString(isEnabled));
} catch (RemoteException e) {
@@ -219,7 +219,7 @@
try {
boolean enable = Boolean.parseBoolean(arg);
- mBmgr.setBackupEnabled(enable);
+ mBmgr.setBackupEnabledForUser(userId, enable);
System.out.println("Backup Manager now " + enableToString(enable));
} catch (NumberFormatException e) {
showUsage();
@@ -232,7 +232,7 @@
void doRun(@UserIdInt int userId) {
try {
- mBmgr.backupNow();
+ mBmgr.backupNowForUser(userId);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -416,7 +416,8 @@
(monitorState != Monitor.OFF)
? new BackupMonitor(monitorState == Monitor.VERBOSE)
: null;
- int err = mBmgr.requestBackup(
+ int err = mBmgr.requestBackupForUser(
+ userId,
packages.toArray(new String[packages.size()]),
observer,
monitor,
@@ -477,7 +478,7 @@
String arg = nextArg();
if ("backups".equals(arg)) {
try {
- mBmgr.cancelBackups();
+ mBmgr.cancelBackupsForUser(userId);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 52a2ab4..55dbc17 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -557,7 +557,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Bundle result = provider.call(null, mMethod, mArg, mExtras);
+ Bundle result = provider.call(null, mUri.getAuthority(), mMethod, mArg, mExtras);
if (result != null) {
result.size(); // unpack
}
diff --git a/cmds/device_config/Android.mk b/cmds/device_config/Android.mk
new file mode 100644
index 0000000..4041e01
--- /dev/null
+++ b/cmds/device_config/Android.mk
@@ -0,0 +1,10 @@
+# Copyright 2018 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := device_config
+LOCAL_SRC_FILES := device_config
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/device_config/device_config b/cmds/device_config/device_config
new file mode 100755
index 0000000..a949bd5
--- /dev/null
+++ b/cmds/device_config/device_config
@@ -0,0 +1,2 @@
+#!/system/bin/sh
+cmd device_config "$@"
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index b3e287b..d4fdf85 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -45,6 +45,7 @@
static struct {
jmethodID onDeviceOpen;
+ jmethodID onDeviceGetReport;
jmethodID onDeviceError;
} gDeviceCallbackClassInfo;
@@ -82,6 +83,13 @@
checkAndClearException(env, "onDeviceOpen");
}
+void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) {
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport,
+ requestId, reportId);
+ checkAndClearException(env, "onDeviceGetReport");
+}
+
JNIEnv* DeviceCallback::getJNIEnv() {
JNIEnv* env;
mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
@@ -103,8 +111,7 @@
return nullptr;
}
- struct uhid_event ev;
- memset(&ev, 0, sizeof(ev));
+ struct uhid_event ev = {};
ev.type = UHID_CREATE2;
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
memcpy(&ev.u.create2.rd_data, descriptor.data(),
@@ -152,8 +159,7 @@
} else {
LOGE("Could not remove fd, ALooper_forThread() returned NULL!");
}
- struct uhid_event ev;
- memset(&ev, 0, sizeof(ev));
+ struct uhid_event ev = {};
ev.type = UHID_DESTROY;
TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
::close(mFd);
@@ -166,8 +172,7 @@
return;
}
- struct uhid_event ev;
- memset(&ev, 0, sizeof(ev));
+ struct uhid_event ev = {};
ev.type = UHID_INPUT2;
ev.u.input2.size = report.size();
memcpy(&ev.u.input2.data, report.data(), report.size() * sizeof(ev.u.input2.data[0]));
@@ -177,6 +182,20 @@
}
}
+void Device::sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const {
+ struct uhid_event ev = {};
+ ev.type = UHID_GET_REPORT_REPLY;
+ ev.u.get_report_reply.id = id;
+ ev.u.get_report_reply.err = report.size() == 0 ? EIO : 0;
+ ev.u.get_report_reply.size = report.size();
+ memcpy(&ev.u.get_report_reply.data, report.data(),
+ report.size() * sizeof(ev.u.get_report_reply.data[0]));
+ ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
+ if (ret < 0 || ret != sizeof(ev)) {
+ LOGE("Failed to send hid event (UHID_GET_REPORT_REPLY): %s", strerror(errno));
+ }
+}
+
int Device::handleEvents(int events) {
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
LOGE("uhid node was closed or an error occurred. events=0x%x", events);
@@ -193,6 +212,11 @@
if (ev.type == UHID_OPEN) {
mDeviceCallback->onDeviceOpen();
+ } else if (ev.type == UHID_GET_REPORT) {
+ mDeviceCallback->onDeviceGetReport(ev.u.get_report.id, ev.u.get_report.rnum);
+ } else if (ev.type == UHID_SET_REPORT) {
+ LOGE("UHID_SET_REPORT is currently not supported");
+ return 0;
}
return 1;
@@ -201,9 +225,13 @@
} // namespace uhid
std::vector<uint8_t> getData(JNIEnv* env, jbyteArray javaArray) {
+ std::vector<uint8_t> data;
+ if (javaArray == nullptr) {
+ return data;
+ }
+
ScopedByteArrayRO scopedArray(env, javaArray);
size_t size = scopedArray.size();
- std::vector<uint8_t> data;
data.reserve(size);
for (size_t i = 0; i < size; i++) {
data.push_back(static_cast<uint8_t>(scopedArray[i]));
@@ -237,6 +265,17 @@
}
}
+static void sendGetFeatureReportReply(JNIEnv* env, jclass /* clazz */, jlong ptr, jint id,
+ jbyteArray rawReport) {
+ uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
+ if (d) {
+ std::vector<uint8_t> report = getData(env, rawReport);
+ d->sendGetFeatureReportReply(id, report);
+ } else {
+ LOGE("Could not send get feature report reply, Device* is null!");
+ }
+}
+
static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
if (d) {
@@ -250,6 +289,8 @@
"Lcom/android/commands/hid/Device$DeviceCallback;)J",
reinterpret_cast<void*>(openDevice) },
{ "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) },
+ { "nativeSendGetFeatureReportReply", "(JI[B)V",
+ reinterpret_cast<void*>(sendGetFeatureReportReply) },
{ "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) },
};
@@ -261,6 +302,8 @@
}
uhid::gDeviceCallbackClassInfo.onDeviceOpen =
env->GetMethodID(clazz, "onDeviceOpen", "()V");
+ uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
+ env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
uhid::gDeviceCallbackClassInfo.onDeviceError =
env->GetMethodID(clazz, "onDeviceError", "()V");
if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 61a1f76..892c7cd 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -28,6 +28,7 @@
~DeviceCallback();
void onDeviceOpen();
+ void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
void onDeviceError();
private:
@@ -45,6 +46,7 @@
~Device();
void sendReport(const std::vector<uint8_t>& report) const;
+ void sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const;
void close();
int handleEvents(int events);
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 8c52a8e..616d411 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -23,6 +23,7 @@
import android.os.MessageQueue;
import android.os.SystemClock;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
@@ -31,11 +32,14 @@
private static final int MSG_OPEN_DEVICE = 1;
private static final int MSG_SEND_REPORT = 2;
- private static final int MSG_CLOSE_DEVICE = 3;
+ private static final int MSG_SEND_GET_FEATURE_REPORT_REPLY = 3;
+ private static final int MSG_CLOSE_DEVICE = 4;
private final int mId;
private final HandlerThread mThread;
private final DeviceHandler mHandler;
+ // mFeatureReports is limited to 256 entries, because the report number is 8-bit
+ private final SparseArray<byte[]> mFeatureReports;
private long mTimeToSend;
private final Object mCond = new Object();
@@ -47,13 +51,16 @@
private static native long nativeOpenDevice(String name, int id, int vid, int pid,
byte[] descriptor, DeviceCallback callback);
private static native void nativeSendReport(long ptr, byte[] data);
+ private static native void nativeSendGetFeatureReportReply(long ptr, int id, byte[] data);
private static native void nativeCloseDevice(long ptr);
- public Device(int id, String name, int vid, int pid, byte[] descriptor, byte[] report) {
+ public Device(int id, String name, int vid, int pid, byte[] descriptor,
+ byte[] report, SparseArray<byte[]> featureReports) {
mId = id;
mThread = new HandlerThread("HidDeviceHandler");
mThread.start();
mHandler = new DeviceHandler(mThread.getLooper());
+ mFeatureReports = featureReports;
SomeArgs args = SomeArgs.obtain();
args.argi1 = id;
args.argi2 = vid;
@@ -113,6 +120,13 @@
Log.e(TAG, "Tried to send report to closed device.");
}
break;
+ case MSG_SEND_GET_FEATURE_REPORT_REPLY:
+ if (mPtr != 0) {
+ nativeSendGetFeatureReportReply(mPtr, msg.arg1, (byte[]) msg.obj);
+ } else {
+ Log.e(TAG, "Tried to send feature report reply to closed device.");
+ }
+ break;
case MSG_CLOSE_DEVICE:
if (mPtr != 0) {
nativeCloseDevice(mPtr);
@@ -145,6 +159,23 @@
mHandler.resumeEvents();
}
+ public void onDeviceGetReport(int requestId, int reportId) {
+ byte[] report = mFeatureReports.get(reportId);
+
+ if (report == null) {
+ Log.e(TAG, "Requested feature report " + reportId + " is not specified");
+ }
+
+ Message msg;
+ msg = mHandler.obtainMessage(MSG_SEND_GET_FEATURE_REPORT_REPLY, requestId, 0, report);
+
+ // Message is set to asynchronous so it won't be blocked by synchronization
+ // barrier during UHID_OPEN. This is necessary for drivers that do
+ // UHID_GET_REPORT requests during probe.
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, mTimeToSend);
+ }
+
public void onDeviceError() {
Log.e(TAG, "Device error occurred, closing /dev/uhid");
Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE);
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index c6a37bd..746e372 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -19,6 +19,7 @@
import android.util.JsonReader;
import android.util.JsonToken;
import android.util.Log;
+import android.util.SparseArray;
import java.io.InputStreamReader;
import java.io.IOException;
@@ -39,6 +40,7 @@
private int mVid;
private int mPid;
private byte[] mReport;
+ private SparseArray<byte[]> mFeatureReports;
private int mDuration;
public int getId() {
@@ -69,6 +71,10 @@
return mReport;
}
+ public SparseArray<byte[]> getFeatureReports() {
+ return mFeatureReports;
+ }
+
public int getDuration() {
return mDuration;
}
@@ -81,6 +87,7 @@
+ ", vid=" + mVid
+ ", pid=" + mPid
+ ", report=" + Arrays.toString(mReport)
+ + ", feature_reports=" + mFeatureReports.toString()
+ ", duration=" + mDuration
+ "}";
}
@@ -112,6 +119,10 @@
mEvent.mReport = report;
}
+ public void setFeatureReports(SparseArray<byte[]> reports) {
+ mEvent.mFeatureReports = reports;
+ }
+
public void setVid(int vid) {
mEvent.mVid = vid;
}
@@ -185,6 +196,9 @@
case "report":
eb.setReport(readData());
break;
+ case "feature_reports":
+ eb.setFeatureReports(readFeatureReports());
+ break;
case "duration":
eb.setDuration(readInt());
break;
@@ -234,6 +248,47 @@
return Integer.decode(val);
}
+ private SparseArray<byte[]> readFeatureReports()
+ throws IllegalStateException, IOException {
+ SparseArray<byte[]> featureReports = new SparseArray();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ // If "id" is not specified, it defaults to 0, which means
+ // report does not contain report ID (based on HID specs).
+ int id = 0;
+ byte[] data = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "id":
+ id = readInt();
+ break;
+ case "data":
+ data = readData();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in feature report: "
+ + name);
+ }
+ }
+ mReader.endObject();
+ if (data != null)
+ featureReports.put(id, data);
+ }
+ mReader.endArray();
+ } catch (IllegalStateException|NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ } finally {
+ return featureReports;
+ }
+ }
+
private void consumeRemainingElements() throws IOException {
while (mReader.hasNext()) {
mReader.skipValue();
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 234e47f..54ac1b0 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -119,7 +119,7 @@
}
int id = e.getId();
Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
- e.getDescriptor(), e.getReport());
+ e.getDescriptor(), e.getReport(), e.getFeatureReports());
mDevices.append(id, d);
}
diff --git a/cmds/incident_helper/OWNERS b/cmds/incident_helper/OWNERS
index 1a68a32..cede4ea 100644
--- a/cmds/incident_helper/OWNERS
+++ b/cmds/incident_helper/OWNERS
@@ -1,2 +1,3 @@
-jinyithu@google.com
+joeo@google.com
kwekua@google.com
+yanmin@google.com
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
index f92d813..573ca4f 100644
--- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -84,9 +84,9 @@
record1->set_event_count(8);
record1->set_wakeup_count(0);
record1->set_expire_count(0);
- record1->set_active_since(0l);
- record1->set_total_time(0l);
- record1->set_max_time(0l);
+ record1->set_active_since(0L);
+ record1->set_total_time(0L);
+ record1->set_max_time(0L);
record1->set_last_change(131348LL);
record1->set_prevent_suspend_time(0LL);
@@ -96,9 +96,9 @@
record2->set_event_count(143);
record2->set_wakeup_count(0);
record2->set_expire_count(0);
- record2->set_active_since(0l);
- record2->set_total_time(123l);
- record2->set_max_time(3l);
+ record2->set_active_since(0L);
+ record2->set_total_time(123L);
+ record2->set_max_time(3L);
record2->set_last_change(2067286206LL);
record2->set_prevent_suspend_time(0LL);
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
index 1e970f4..40da583 100644
--- a/cmds/incidentd/Android.bp
+++ b/cmds/incidentd/Android.bp
@@ -53,6 +53,7 @@
"libprotoutil",
"libservices",
"libutils",
+ "libprotobuf-cpp-lite",
],
init_rc: ["incidentd.rc"],
diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS
index 1a68a32..cede4ea 100644
--- a/cmds/incidentd/OWNERS
+++ b/cmds/incidentd/OWNERS
@@ -1,2 +1,3 @@
-jinyithu@google.com
+joeo@google.com
kwekua@google.com
+yanmin@google.com
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index a8ef831..04819ec 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -18,7 +18,7 @@
#include "FdBuffer.h"
-#include <cutils/log.h>
+#include <log/log.h>
#include <utils/SystemClock.h>
#include <fcntl.h>
@@ -47,9 +47,13 @@
while (true) {
if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
mTruncated = true;
+ VLOG("Truncating data");
break;
}
- if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
+ if (mBuffer.writeBuffer() == NULL) {
+ VLOG("No memory");
+ return NO_MEMORY;
+ }
int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
if (remainingTime <= 0) {
@@ -58,7 +62,7 @@
break;
}
- int count = poll(&pfds, 1, remainingTime);
+ int count = TEMP_FAILURE_RETRY(poll(&pfds, 1, remainingTime));
if (count == 0) {
VLOG("timed out due to block calling poll");
mTimedOut = true;
@@ -102,7 +106,10 @@
VLOG("Truncating data");
break;
}
- if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
+ if (mBuffer.writeBuffer() == NULL) {
+ VLOG("No memory");
+ return NO_MEMORY;
+ }
ssize_t amt =
TEMP_FAILURE_RETRY(::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite()));
@@ -144,10 +151,14 @@
// This is the buffer used to store processed data
while (true) {
if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ VLOG("Truncating data");
mTruncated = true;
break;
}
- if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
+ if (mBuffer.writeBuffer() == NULL) {
+ VLOG("No memory");
+ return NO_MEMORY;
+ }
int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
if (remainingTime <= 0) {
@@ -157,7 +168,7 @@
}
// wait for any pfds to be ready to perform IO
- int count = poll(pfds, 3, remainingTime);
+ int count = TEMP_FAILURE_RETRY(poll(pfds, 3, remainingTime));
if (count == 0) {
VLOG("timed out due to block calling poll");
mTimedOut = true;
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index b23c1ed..f8fb4a6 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -29,7 +29,7 @@
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
#include <binder/IShellCallback.h>
-#include <cutils/log.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
@@ -42,13 +42,11 @@
#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB
#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
-// Skip logs (1100 - 1108) because they are already in the bug report
-// Skip 1200, 1201, 1202, 3018 because they take too long
-// TODO(120079956): Skip 3008, 3015 because of error
+// Skip these sections for dumpstate only. Dumpstate allows 10s max for each service to dump.
+// Skip logs (1100 - 1108) and traces (1200 - 1202) because they are already in the bug report.
+// Skip 3018 because it takes too long.
#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
1200, 1201, 1202, /* Native, hal, java traces */ \
- 3008, /* "package --proto" */ \
- 3015, /* "activity --proto processes" */ \
3018 /* "meminfo -a --proto" */ }
namespace android {
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 7a8ebe3..08f535d 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <android/util/protobuf.h>
-#include <cutils/log.h>
+#include <log/log.h>
namespace android {
namespace os {
@@ -96,7 +96,12 @@
uint64_t token = mProto.start(encode_field_id(policy));
while (mData.rp()->pos() - start != msgSize) {
status_t err = stripField(policy, spec, depth + 1);
- if (err != NO_ERROR) return err;
+ 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;
@@ -117,9 +122,13 @@
}
while (mData.hasNext()) {
status_t err = stripField(mPolicy, spec, 0);
- if (err != NO_ERROR) return err;
+ if (err != NO_ERROR) return err; // Error logged in stripField.
}
- if (mData.bytesRead() != mData.size()) return BAD_VALUE;
+ 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;
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index b3bab0c..8f62da2 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -129,6 +129,7 @@
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", "");
@@ -180,12 +181,12 @@
for (const Section** section = SECTION_LIST; *section; section++) {
const int id = (*section)->id;
if ((*section)->userdebugAndEngOnly && !isUserdebugOrEng) {
- ALOGD("Skipping incident report section %d '%s' because it's limited to userdebug/eng",
+ VLOG("Skipping incident report section %d '%s' because it's limited to userdebug/eng",
id, (*section)->name.string());
continue;
}
if (this->batch.containsSection(id)) {
- ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
+ 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(
@@ -198,11 +199,12 @@
int64_t startTime = uptimeMillis();
err = (*section)->Execute(&batch);
int64_t endTime = uptimeMillis();
- stats->set_success(err == NO_ERROR);
stats->set_exec_duration_ms(endTime - startTime);
if (err != NO_ERROR) {
ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
(*section)->name.string(), id, strerror(-err));
+ // Execute() has already recorded this status. Only update if there's new failure.
+ stats->set_success(false);
goto DONE;
}
(*reportByteSize) += stats->report_size_bytes();
@@ -214,11 +216,13 @@
id, IIncidentReportStatusListener::STATUS_FINISHED);
}
}
- ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
+ VLOG("Finish incident report section %d '%s'", id, (*section)->name.string());
+ sectionCount++;
}
}
DONE:
+ ALOGD("Incident reporting took %d sections.", sectionCount);
// Reports the metdadata when taking the incident report.
if (!isTest) metadataSection.Execute(&batch);
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index cd48af9..10d2268 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -75,6 +75,7 @@
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.
@@ -83,7 +84,8 @@
status_t err = -EBADF;
EncodedBuffer::iterator data = buffer.data();
PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
- int writeable = 0;
+ IncidentMetadata::SectionStats* stats = requests->sectionStats(id);
+ int nPassed = 0;
// The streaming ones, group requests by spec in order to save unnecessary strip operations
map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
@@ -99,7 +101,18 @@
for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
PrivacySpec spec = mit->first;
err = privacyBuffer.strip(spec);
- if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted.
+ if (err != NO_ERROR) {
+ // 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++) {
@@ -114,7 +127,7 @@
request->err = err;
continue;
}
- writeable++;
+ nPassed++;
VLOG("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(),
request->fd, spec.dest);
}
@@ -125,7 +138,15 @@
if (requests->mainFd() >= 0) {
PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest());
err = privacyBuffer.strip(spec);
- if (err != NO_ERROR) return err; // the buffer data is corrupted.
+ if (err != NO_ERROR) {
+ 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());
@@ -138,7 +159,7 @@
requests->setMainFd(-1);
goto DONE;
}
- writeable++;
+ 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.
@@ -147,7 +168,7 @@
DONE:
// only returns error if there is no fd to write to.
- return writeable > 0 ? NO_ERROR : err;
+ return nPassed > 0 ? NO_ERROR : err;
}
// ================================================================================
@@ -213,6 +234,8 @@
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);
}
@@ -439,8 +462,9 @@
status_t err = NO_ERROR;
pthread_t thread;
pthread_attr_t attr;
- bool timedOut = false;
+ bool workerDone = false;
FdBuffer buffer;
+ IncidentMetadata::SectionStats* stats = requests->sectionStats(this->id);
// Data shared between this thread and the worker thread.
sp<WorkerThreadData> data = new WorkerThreadData(this);
@@ -475,8 +499,7 @@
// Loop reading until either the timeout or the worker side is done (i.e. eof).
err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
if (err != NO_ERROR) {
- // TODO: Log this error into the incident report.
- ALOGW("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
+ ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
}
// Done with the read fd. The worker thread closes the write one so
@@ -484,31 +507,32 @@
data->pipe.readFd().reset();
// If the worker side is finished, then return its error (which may overwrite
- // our possible error -- but it's more interesting anyway). If not, then we timed out.
+ // our possible error -- but it's more interesting anyway). If not, then we timed out.
{
unique_lock<mutex> lock(data->lock);
- if (!data->workerDone) {
- // We timed out
- timedOut = true;
- } else {
- if (data->workerError != NO_ERROR) {
- err = data->workerError;
- // TODO: Log this error into the incident report.
- ALOGW("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
- }
+ if (data->workerError != NO_ERROR) {
+ err = data->workerError;
+ ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
}
+ workerDone = data->workerDone;
}
- write_section_stats(requests->sectionStats(this->id), buffer);
- if (timedOut || buffer.timedOut()) {
- ALOGW("[%s] timed out", this->name.string());
+ write_section_stats(stats, 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);
return NO_ERROR;
}
-
- // TODO: There was an error with the command or buffering. Report that. For now
- // just exit with a log messasge.
- if (err != NO_ERROR) {
- ALOGW("[%s] failed with error '%s'", this->name.string(), strerror(-err));
+ if (buffer.truncated()) {
+ ALOGW("[%s] too large, truncating", this->name.string());
+ // Do not write a truncated section. It won't pass through the PrivacyBuffer.
+ return NO_ERROR;
+ }
+ if (!workerDone || buffer.timedOut()) {
+ ALOGW("[%s] timed out", this->name.string());
return NO_ERROR;
}
@@ -617,14 +641,8 @@
sp<IBinder> service = defaultServiceManager()->checkService(mService);
if (service == NULL) {
- // Returning an error interrupts the entire incident report, so just
- // log the failure.
- // TODO: have a meta record inside the report that would log this
- // failure inside the report, because the fact that we can't find
- // the service is good data in and of itself. This is running in
- // another thread so lock that carefully...
ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
- return NO_ERROR;
+ return NAME_NOT_FOUND;
}
service->dump(pipeWriteFd, mArgs);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 7fa05be..04173b2 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -424,13 +424,14 @@
dprintf(out, "\n be removed from memory and disk!\n");
dprintf(out, "\n");
dprintf(out,
- "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] "
- "[--proto]\n");
+ "usage: adb shell cmd stats dump-report [UID] NAME [--keep_data] "
+ "[--include_current_bucket] [--proto]\n");
dprintf(out, " Dump all metric data for a configuration.\n");
dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
dprintf(out, " calling uid is used.\n");
dprintf(out, " NAME The name of the configuration\n");
+ dprintf(out, " --keep_data Do NOT erase the data upon dumping it.\n");
dprintf(out, " --proto Print proto binary.\n");
dprintf(out, "\n");
dprintf(out, "\n");
@@ -590,6 +591,7 @@
bool good = false;
bool proto = false;
bool includeCurrentBucket = false;
+ bool eraseData = true;
int uid;
string name;
if (!std::strcmp("--proto", args[argCount-1].c_str())) {
@@ -600,6 +602,10 @@
includeCurrentBucket = true;
argCount -= 1;
}
+ if (!std::strcmp("--keep_data", args[argCount-1].c_str())) {
+ eraseData = false;
+ argCount -= 1;
+ }
if (argCount == 2) {
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
@@ -627,7 +633,7 @@
if (good) {
vector<uint8_t> data;
mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
- includeCurrentBucket, true /* erase_data */, ADB_DUMP, &data);
+ includeCurrentBucket, eraseData, ADB_DUMP, &data);
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
dprintf(out, "%c", data[i]);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 41a2021..f58caff 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -30,6 +30,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/stats/enums.proto";
+import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto";
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
@@ -159,6 +160,15 @@
PhenotypeFlagStateChanged phenotype_flag_state_changed = 101;
BinaryPushStateChanged binary_push_state_changed = 102;
DevicePolicyEvent device_policy_event = 103;
+ DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104;
+ DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported = 105;
+ DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106;
+ DocsUIFileOperationReported docs_ui_provider_file_op = 107;
+ DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request = 108;
+ DocsUILaunchReported docs_ui_launch_reported = 109;
+ DocsUIRootVisitedReported docs_ui_root_visited = 110;
+ DocsUIStartupMsReported docs_ui_startup_ms = 111;
+ DocsUIUserActionReported docs_ui_user_action_reported = 112;
}
// Pulled events will start at field 10000.
@@ -1207,6 +1217,12 @@
optional bool has_audio = 3;
optional bool has_hid = 4;
optional bool has_storage = 5;
+ enum State {
+ STATE_DISCONNECTED = 0;
+ STATE_CONNECTED = 1;
+ }
+ optional State state = 6;
+ optional int64 last_connect_duration_ms = 7;
}
@@ -1255,14 +1271,18 @@
* Logs when something is plugged into or removed from the USB-C connector.
*
* Logged from:
- * Vendor USB HAL.
+ * UsbService
*/
message UsbConnectorStateChanged {
enum State {
- DISCONNECTED = 0;
- CONNECTED = 1;
+ STATE_DISCONNECTED = 0;
+ STATE_CONNECTED = 1;
}
optional State state = 1;
+ optional string id = 2;
+ // Last active session in ms.
+ // 0 when the port is in connected state.
+ optional int64 last_connect_duration_millis = 3;
}
/**
@@ -1501,17 +1521,17 @@
/**
* Where the action is happening
*/
- optional android.app.settings.PageId pageId = 3;
+ optional android.app.settings.PageId page_id = 3;
/**
* What preference changed in this event.
*/
- optional string changedPreferenceKey = 4;
+ optional string changed_preference_key = 4;
/**
* The new value of the changed preference.
*/
- optional int64 changedPreferenceIntValue = 5;
+ optional int64 changed_preference_int_value = 5;
}
/**
@@ -3467,3 +3487,103 @@
// A parameter specifying a list of package names, bundle extras or string parameters.
optional android.stats.devicepolicy.StringList string_list_value = 6 [(log_mode) = MODE_BYTES];
}
+
+/**
+ * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUILaunchReported {
+ optional android.stats.docsui.LaunchAction launch_action = 1;
+ optional bool has_initial_uri = 2;
+ optional android.stats.docsui.MimeType mime_type = 3;
+ optional android.stats.docsui.Root initial_root = 4;
+}
+
+/**
+ * Logs root/app visited event in file managers/picker. Call this when the user
+ * taps on root/app in hamburger menu.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIRootVisitedReported {
+ optional android.stats.docsui.ContextScope scope = 1;
+ optional android.stats.docsui.Root root = 2;
+}
+
+/**
+ * Logs file operation stats. Call this when a file operation has completed.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIFileOperationReported {
+ optional android.stats.docsui.Provider provider = 1;
+ optional android.stats.docsui.FileOperation file_op = 2;
+}
+
+/**
+ * Logs file operation stats. Call this when a copy/move operation has completed with a specific
+ * mode.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIFileOperationCopyMoveModeReported {
+ optional android.stats.docsui.FileOperation file_op = 1;
+ optional android.stats.docsui.CopyMoveOpMode mode = 2;
+}
+
+
+/**
+ * Logs file sub operation stats. Call this when a file operation has failed.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIFileOperationFailureReported {
+ optional android.stats.docsui.Authority authority = 1;
+ optional android.stats.docsui.SubFileOperation sub_op = 2;
+}
+
+/**
+* Logs the cancellation of a file operation. Call this when a job is canceled
+*
+* Logged from:
+* package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+*/
+message DocsUIFileOperationCanceledReported {
+ optional android.stats.docsui.FileOperation file_op = 1;
+}
+
+/**
+ * Logs startup time in milliseconds.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIStartupMsReported {
+ optional int32 startup_millis = 1;
+}
+
+/**
+ * Logs the action that was started by user.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIUserActionReported {
+ optional android.stats.docsui.UserAction action = 1;
+}
+
+/**
+ * Logs the invalid type when invalid scoped access is requested.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/ScopedAccessMetrics.java
+ */
+message DocsUIInvalidScopedAccessRequestReported {
+ optional android.stats.docsui.InvalidScopedAccess type = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index f501574..7043d66 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -59,15 +59,21 @@
mLastPullTimeNs = elapsedTimeNs;
int64_t pullStartTimeNs = getElapsedRealtimeNs();
bool ret = PullInternal(&mCachedData);
+ if (!ret) {
+ mCachedData.clear();
+ return false;
+ }
StatsdStats::getInstance().notePullTime(mTagId, getElapsedRealtimeNs() - pullStartTimeNs);
for (const shared_ptr<LogEvent>& data : mCachedData) {
data->setElapsedTimestampNs(elapsedTimeNs);
data->setLogdWallClockTimestampNs(wallClockTimeNs);
}
- if (ret && mCachedData.size() > 0) {
+
+ if (mCachedData.size() > 0) {
mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
(*data) = mCachedData;
}
+
StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
return ret;
}
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 22cb2f5..cafd797 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -39,6 +39,7 @@
// Pulls the data. The returned data will have elapsedTimeNs set as timeNs
// and will have wallClockTimeNs set as current wall clock time.
+ // Return true if the pull is successful.
bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5ca8814..14f2de0 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -49,6 +49,8 @@
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
+
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for CountMetricData
@@ -151,10 +153,13 @@
} else {
flushIfNeededLocked(dumpTimeNs);
}
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
+
if (mPastBuckets.empty()) {
return;
}
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 35deffe..7797bd9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -48,6 +48,7 @@
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for DurationMetricData
@@ -461,12 +462,14 @@
} else {
flushIfNeededLocked(dumpTimeNs);
}
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
if (mPastBuckets.empty()) {
VLOG(" Duration metric, empty return");
return;
}
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index a18e406..31a4361 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -44,6 +44,7 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_EVENT_METRICS = 4;
+const int FIELD_ID_IS_ACTIVE = 13;
// for EventMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for EventMetricData
@@ -108,10 +109,11 @@
const bool erase_data,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mProto->size() <= 0) {
return;
}
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
size_t bufferSize = mProto->size();
VLOG("metric %lld dump report now... proto size: %zu ",
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 3a34743..03e42ce 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -49,6 +49,7 @@
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
@@ -192,11 +193,13 @@
flushIfNeededLocked(dumpTimeNs);
}
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
if (mPastBuckets.empty()) {
return;
}
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 127cbbd..09e2409 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -223,6 +223,10 @@
void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
+ inline bool isActiveLocked() const {
+ return mIsActive;
+ }
+
/**
* Flushes the current bucket if the eventTime is after the current bucket's end time. This will
also flush the current partial bucket in memory.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index a34df8aa..1f22a6a 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -52,6 +52,7 @@
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
// for ValueMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
@@ -72,17 +73,15 @@
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
+const Value ZERO_LONG((int64_t)0);
+const Value ZERO_DOUBLE((int64_t)0);
+
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
-ValueMetricProducer::ValueMetricProducer(const ConfigKey& key,
- const ValueMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& conditionWizard,
- const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId,
- const int64_t timeBaseNs,
- const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager)
+ValueMetricProducer::ValueMetricProducer(
+ const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
@@ -102,7 +101,9 @@
mAggregationType(metric.aggregation_type()),
mUseDiff(metric.has_use_diff() ? metric.use_diff() : (mIsPulled ? true : false)),
mValueDirection(metric.value_direction()),
- mSkipZeroDiffOutput(metric.skip_zero_diff_output()) {
+ mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
+ mUseZeroDefaultBase(metric.use_zero_default_base()),
+ mHasGlobalBase(false) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -190,10 +191,12 @@
} else {
flushIfNeededLocked(dumpTimeNs);
}
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
}
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
// Fills the dimension path if not slicing by ALL.
@@ -302,6 +305,15 @@
}
}
+void ValueMetricProducer::resetBase() {
+ for (auto& slice : mCurrentSlicedBucket) {
+ for (auto& interval : slice.second) {
+ interval.hasBase = false;
+ }
+ }
+ mHasGlobalBase = false;
+}
+
void ValueMetricProducer::onConditionChangedLocked(const bool condition,
const int64_t eventTimeNs) {
if (eventTimeNs < mCurrentBucketStartTimeNs) {
@@ -317,13 +329,10 @@
pullAndMatchEventsLocked(eventTimeNs);
}
- // when condition change from true to false, clear diff base
+ // when condition change from true to false, clear diff base but don't
+ // reset other counters as we may accumulate more value in the bucket.
if (mUseDiff && mCondition && !condition) {
- for (auto& slice : mCurrentSlicedBucket) {
- for (auto& interval : slice.second) {
- interval.hasBase = false;
- }
- }
+ resetBase();
}
mCondition = condition;
@@ -332,15 +341,17 @@
void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
vector<std::shared_ptr<LogEvent>> allData;
if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
- if (allData.size() == 0) {
- return;
- }
for (const auto& data : allData) {
if (mEventMatcherWizard->matchLogEvent(
*data, mWhatMatcherIndex) == MatchingState::kMatched) {
onMatchedLogEventLocked(mWhatMatcherIndex, *data);
}
}
+ mHasGlobalBase = true;
+ } else {
+ // for pulled data, every pull is needed. So we reset the base if any
+ // pull fails.
+ resetBase();
}
}
@@ -376,6 +387,7 @@
onMatchedLogEventLocked(mWhatMatcherIndex, *data);
}
}
+ mHasGlobalBase = true;
} else {
VLOG("No need to commit data on condition false.");
}
@@ -420,6 +432,26 @@
return false;
}
+bool ValueMetricProducer::hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey) {
+ // ===========GuardRail==============
+ // 1. Report the tuple count if the tuple count > soft limit
+ if (mCurrentFullBucket.find(newKey) != mCurrentFullBucket.end()) {
+ return false;
+ }
+ if (mCurrentFullBucket.size() > mDimensionSoftLimit - 1) {
+ size_t newTupleCount = mCurrentFullBucket.size() + 1;
+ // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+ if (newTupleCount > mDimensionHardLimit) {
+ ALOGE("ValueMetric %lld dropping data for full bucket dimension key %s",
+ (long long)mMetricId,
+ newKey.toString().c_str());
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) {
for (const FieldValue& value : event.getValues()) {
if (value.mField.matches(matcher)) {
@@ -484,13 +516,21 @@
VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
return;
}
+ interval.seenNewData = true;
if (mUseDiff) {
- // no base. just update base and return.
if (!interval.hasBase) {
- interval.base = value;
- interval.hasBase = true;
- return;
+ if (mHasGlobalBase && mUseZeroDefaultBase) {
+ // The bucket has global base. This key does not.
+ // Optionally use zero as base.
+ interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
+ interval.hasBase = true;
+ } else {
+ // no base. just update base and return.
+ interval.base = value;
+ interval.hasBase = true;
+ return;
+ }
}
Value diff;
switch (mValueDirection) {
@@ -580,11 +620,7 @@
if (numBucketsForward > 1) {
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
// take base again in future good bucket.
- for (auto& slice : mCurrentSlicedBucket) {
- for (auto& interval : slice.second) {
- interval.hasBase = false;
- }
- }
+ resetBase();
}
VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
(long long)mCurrentBucketStartTimeNs);
@@ -633,6 +669,9 @@
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
+ if (hitFullBucketGuardRailLocked(slice.first)) {
+ continue;
+ }
// TODO: fix this when anomaly can accept double values
mCurrentFullBucket[slice.first] += slice.second[0].value.long_value;
}
@@ -664,11 +703,21 @@
}
}
- // Reset counters
- for (auto& slice : mCurrentSlicedBucket) {
- for (auto& interval : slice.second) {
+ for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
+ bool obsolete = true;
+ for (auto& interval : it->second) {
interval.hasValue = false;
interval.sampleSize = 0;
+ if (interval.seenNewData) {
+ obsolete = false;
+ }
+ interval.seenNewData = false;
+ }
+
+ if (obsolete) {
+ it = mCurrentSlicedBucket.erase(it);
+ } else {
+ it++;
}
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 9fe84dc..4991af4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -128,7 +128,9 @@
int sampleSize;
// If this dimension has any non-tainted value. If not, don't report the
// dimension.
- bool hasValue;
+ bool hasValue = false;
+ // Whether new data is seen in the bucket.
+ bool seenNewData = false;
} Interval;
std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
@@ -146,8 +148,13 @@
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+ bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
+
void pullAndMatchEventsLocked(const int64_t timestampNs);
+ // Reset diff base and mHasGlobalBase
+ void resetBase();
+
static const size_t kBucketSize = sizeof(ValueBucket{});
const size_t mDimensionSoftLimit;
@@ -164,6 +171,18 @@
const bool mSkipZeroDiffOutput;
+ // If true, use a zero value as base to compute the diff.
+ // This is used for new keys which are present in the new data but was not
+ // present in the base data.
+ // The default base will only be used if we have a global base.
+ const bool mUseZeroDefaultBase;
+
+ // For pulled metrics, this is always set to true whenever a pull succeeds.
+ // It is set to false when a pull fails, or upon condition change to false.
+ // This is used to decide if we have the right base data to compute the
+ // diff against.
+ bool mHasGlobalBase;
+
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
@@ -185,6 +204,9 @@
FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
+ FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
+ FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+ FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
};
} // namespace statsd
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 32ee5af..a6f27c8 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -219,6 +219,8 @@
optional DimensionsValue dimensions_path_in_what = 11;
optional DimensionsValue dimensions_path_in_condition = 12;
+
+ optional bool is_active = 13;
}
message UidMapping {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 61854a4..f485185 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -274,6 +274,8 @@
optional bool use_diff = 12;
+ optional bool use_zero_default_base = 15 [default = false];
+
enum ValueDirection {
UNKNOWN = 0;
INCREASING = 1;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 355df29..237f8b9 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -240,6 +240,49 @@
EXPECT_EQ(2, report.annotation(0).field_int32());
}
+TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
+ // Setup a simple config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(123456);
+ countMetric->set_what(wakelockAcquireMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+
+ ConfigKey cfgKey;
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
+
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 2);
+ processor->OnLogEvent(event.get());
+
+ vector<uint8_t> bytes;
+ ConfigMetricsReportList output;
+
+ // Dump report WITHOUT erasing data.
+ processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, &bytes);
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+ // Dump report WITH erasing data. There should be data since we didn't previously erase it.
+ processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, &bytes);
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+ // Dump report again. There should be no data since we erased it.
+ processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes);
+ output.ParseFromArray(bytes.data(), bytes.size());
+ bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0);
+ EXPECT_TRUE(noData);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 2b0285b..370c36c 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -119,7 +119,8 @@
ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
// Expect no metrics since the bucket has not finished yet.
- EXPECT_EQ(0, report.metrics_size());
+ EXPECT_EQ(1, report.metrics_size());
+ EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
}
TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
@@ -138,7 +139,8 @@
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
- EXPECT_EQ(0, report.metrics_size());
+ EXPECT_EQ(1, report.metrics_size());
+ EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
}
TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 6d1317c..16be3d7 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -262,7 +262,8 @@
// When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as
// one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by
// itself.
- EXPECT_EQ(0, reports.reports(0).metrics_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size());
}
TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) {
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 44aa00b..5524503 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1468,6 +1468,342 @@
EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
+/*
+ * Tests zero default base.
+ */
+TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.set_use_zero_default_base(true);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+ event->write(1);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ auto iter = valueProducer.mCurrentSlicedBucket.begin();
+ auto& interval1 = iter->second[0];
+ EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(3, interval1.base.long_value);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ vector<shared_ptr<LogEvent>> allData;
+
+ allData.clear();
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event1->write(2);
+ event1->write(4);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event2->write(1);
+ event2->write(11);
+ event2->init();
+ allData.push_back(event1);
+ allData.push_back(event2);
+
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(11, interval1.base.long_value);
+ EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(8, interval1.value.long_value);
+
+ auto it = valueProducer.mCurrentSlicedBucket.begin();
+ for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+ if (it != iter) {
+ break;
+ }
+ }
+ EXPECT_TRUE(it != iter);
+ auto& interval2 = it->second[0];
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_EQ(true, interval2.hasValue);
+ EXPECT_EQ(4, interval2.value.long_value);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+}
+
+/*
+ * Tests using zero default base with failed pull.
+ */
+TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.set_use_zero_default_base(true);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+ event->write(1);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ auto iter = valueProducer.mCurrentSlicedBucket.begin();
+ auto& interval1 = iter->second[0];
+ EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(3, interval1.base.long_value);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ vector<shared_ptr<LogEvent>> allData;
+
+ allData.clear();
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event1->write(2);
+ event1->write(4);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event2->write(1);
+ event2->write(11);
+ event2->init();
+ allData.push_back(event1);
+ allData.push_back(event2);
+
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(11, interval1.base.long_value);
+ EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(8, interval1.value.long_value);
+
+ auto it = valueProducer.mCurrentSlicedBucket.begin();
+ for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+ if (it != iter) {
+ break;
+ }
+ }
+ EXPECT_TRUE(it != iter);
+ auto& interval2 = it->second[0];
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_EQ(true, interval2.hasValue);
+ EXPECT_EQ(4, interval2.value.long_value);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ // next pull somehow did not happen, skip to end of bucket 3
+ allData.clear();
+ event1 = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
+ event1->write(2);
+ event1->write(5);
+ event1->init();
+ allData.push_back(event1);
+ valueProducer.onDataPulled(allData);
+
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(5, interval2.base.long_value);
+ EXPECT_EQ(false, interval2.hasValue);
+ EXPECT_EQ(false, interval1.hasBase);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+
+ allData.clear();
+ event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
+ event1->write(2);
+ event1->write(13);
+ event1->init();
+ allData.push_back(event1);
+ event2 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
+ event2->write(1);
+ event2->write(5);
+ event2->init();
+ allData.push_back(event2);
+ valueProducer.onDataPulled(allData);
+
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(13, interval2.base.long_value);
+ EXPECT_EQ(true, interval2.hasValue);
+ EXPECT_EQ(8, interval2.value.long_value);
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(5, interval1.base.long_value);
+ EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(5, interval1.value.long_value);
+ EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+}
+
+/*
+ * Tests trim unused dimension key if no new data is seen in an entire bucket.
+ */
+TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+ event->write(1);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ auto iter = valueProducer.mCurrentSlicedBucket.begin();
+ auto& interval1 = iter->second[0];
+ EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(3, interval1.base.long_value);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ vector<shared_ptr<LogEvent>> allData;
+
+ allData.clear();
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event1->write(2);
+ event1->write(4);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event2->write(1);
+ event2->write(11);
+ event2->init();
+ allData.push_back(event1);
+ allData.push_back(event2);
+
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(11, interval1.base.long_value);
+ EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(8, interval1.value.long_value);
+ EXPECT_TRUE(interval1.seenNewData);
+
+ auto it = valueProducer.mCurrentSlicedBucket.begin();
+ for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+ if (it != iter) {
+ break;
+ }
+ }
+ EXPECT_TRUE(it != iter);
+ auto& interval2 = it->second[0];
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_EQ(false, interval2.hasValue);
+ EXPECT_TRUE(interval2.seenNewData);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ // next pull somehow did not happen, skip to end of bucket 3
+ allData.clear();
+ event1 = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
+ event1->write(2);
+ event1->write(5);
+ event1->init();
+ allData.push_back(event1);
+ valueProducer.onDataPulled(allData);
+
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+
+ EXPECT_EQ(false, interval1.hasBase);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(8, interval1.value.long_value);
+ // on probation now
+ EXPECT_FALSE(interval1.seenNewData);
+
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(5, interval2.base.long_value);
+ EXPECT_EQ(false, interval2.hasValue);
+ // back to good status
+ EXPECT_TRUE(interval2.seenNewData);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+
+ allData.clear();
+ event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
+ event1->write(2);
+ event1->write(13);
+ event1->init();
+ allData.push_back(event1);
+ valueProducer.onDataPulled(allData);
+
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(13, interval2.base.long_value);
+ EXPECT_EQ(true, interval2.hasValue);
+ EXPECT_EQ(8, interval2.value.long_value);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp
new file mode 100644
index 0000000..75a57a3
--- /dev/null
+++ b/cmds/statsd/tools/localtools/Android.bp
@@ -0,0 +1,25 @@
+java_binary_host {
+ name: "statsd_localdrive",
+ manifest: "localdrive_manifest.txt",
+ srcs: [
+ "src/com/android/statsd/shelltools/localdrive/*.java",
+ "src/com/android/statsd/shelltools/Utils.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ "guava",
+ ],
+}
+
+java_binary_host {
+ name: "statsd_testdrive",
+ manifest: "testdrive_manifest.txt",
+ srcs: [
+ "src/com/android/statsd/shelltools/testdrive/*.java",
+ "src/com/android/statsd/shelltools/Utils.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ "guava",
+ ],
+}
\ No newline at end of file
diff --git a/cmds/statsd/tools/localtools/localdrive_manifest.txt b/cmds/statsd/tools/localtools/localdrive_manifest.txt
new file mode 100644
index 0000000..035cea1
--- /dev/null
+++ b/cmds/statsd/tools/localtools/localdrive_manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.statsd.shelltools.localdrive.LocalDrive
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
new file mode 100644
index 0000000..597377e
--- /dev/null
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.shelltools;
+
+import com.android.os.StatsLog.ConfigMetricsReportList;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Formatter;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * Utilities for local use of statsd.
+ */
+public class Utils {
+
+ public static final String CMD_UPDATE_CONFIG = "cmd stats config update";
+ public static final String CMD_DUMP_REPORT = "cmd stats dump-report";
+ public static final String CMD_REMOVE_CONFIG = "cmd stats config remove";
+
+ public static final String SHELL_UID = "2000"; // Use shell, even if rooted.
+
+ /**
+ * Runs adb shell command with output directed to outputFile if non-null.
+ */
+ public static void runCommand(File outputFile, Logger logger, String... commands)
+ throws IOException, InterruptedException {
+ ProcessBuilder pb = new ProcessBuilder(commands);
+ if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
+ pb.redirectOutput(outputFile);
+ }
+ Process process = pb.start();
+
+ // Capture any errors
+ StringBuilder err = new StringBuilder();
+ BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ err.append(line).append('\n');
+ }
+ logger.severe(err.toString());
+
+ // Check result
+ if (process.waitFor() == 0) {
+ logger.fine("Adb command successful.");
+ } else {
+ logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands));
+ throw new RuntimeException("Error running adb command: " + err.toString());
+ }
+ }
+
+ /**
+ * Dumps the report from the device and converts it to a ConfigMetricsReportList.
+ * Erases the data if clearData is true.
+ */
+ public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
+ Logger logger) throws IOException, InterruptedException {
+ try {
+ File outputFile = File.createTempFile("statsdret", ".bin");
+ outputFile.deleteOnExit();
+ runCommand(
+ outputFile,
+ logger,
+ "adb",
+ "shell",
+ CMD_DUMP_REPORT,
+ SHELL_UID,
+ String.valueOf(configId),
+ clearData ? "" : "--keep_data",
+ "--include_current_bucket",
+ "--proto");
+ ConfigMetricsReportList reportList =
+ ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
+ return reportList;
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ logger.severe("Failed to fetch and parse the statsd output report. "
+ + "Perhaps there is not a valid statsd config for the requested "
+ + "uid=" + SHELL_UID
+ + ", configId=" + configId
+ + ".");
+ throw (e);
+ }
+ }
+
+ public static void setUpLogger(Logger logger, boolean debug) {
+ ConsoleHandler handler = new ConsoleHandler();
+ handler.setFormatter(new LocalToolsFormatter());
+ logger.setUseParentHandlers(false);
+ if (debug) {
+ handler.setLevel(Level.ALL);
+ logger.setLevel(Level.ALL);
+ }
+ logger.addHandler(handler);
+ }
+
+ public static class LocalToolsFormatter extends Formatter {
+ public String format(LogRecord record) {
+ return record.getMessage() + "\n";
+ }
+ }
+}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
new file mode 100644
index 0000000..08074ed
--- /dev/null
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.shelltools.localdrive;
+
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.statsd.shelltools.Utils;
+
+import com.google.common.io.Files;
+import com.google.protobuf.TextFormat;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * Tool for using statsd locally. Can upload a config and get the data. Handles
+ * both binary and human-readable protos.
+ * To make: make statsd_localdrive
+ * To run: statsd_localdrive (i.e. ./out/host/linux-x86/bin/statsd_localdrive)
+ */
+public class LocalDrive {
+ private static final boolean DEBUG = false;
+
+ public static final long DEFAULT_CONFIG_ID = 56789;
+
+ public static final String BINARY_FLAG = "--binary";
+ public static final String CLEAR_DATA = "--clear";
+ public static final String NO_UID_MAP_FLAG = "--no-uid-map";
+
+ public static final String HELP_STRING =
+ "Usage:\n\n" +
+
+ "statsd_local upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ " Uploads the given statsd config file (in binary or human-readable-text format).\n" +
+ " If a config with this id already exists, removes it first.\n" +
+ " CONFIG_FILE Location of config file on host.\n" +
+ " CONFIG_ID Long ID to associate with this config. If absent, uses "
+ + DEFAULT_CONFIG_ID + ".\n" +
+ " --binary Config is in binary format; otherwise, assumed human-readable text.\n" +
+ // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
+ "\n" +
+
+ "statsd_local update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ " Same as upload, but does not remove the old config first (if it already exists).\n" +
+ // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
+ "\n" +
+
+ "statsd_local get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+ " Prints the output statslog data (in binary or human-readable-text format).\n" +
+ " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
+ " --binary Output should be in binary, instead of default human-readable text.\n" +
+ " Binary output can be redirected as usual (e.g. > FILENAME).\n" +
+ " --no-uid-map Do not include the uid-map (the very lengthy uid<-->pkgName map).\n" +
+ " --clear Erase the data from statsd afterwards. Does not remove the config.\n" +
+ // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID [--keep_data]
+ // --include_current_bucket --proto
+ "\n" +
+
+ "statsd_local remove [CONFIG_ID]\n" +
+ " Removes the config.\n" +
+ " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
+ // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
+ "\n" +
+
+ "statsd_local clear [CONFIG_ID]\n" +
+ " Clears the data associated with the config.\n" +
+ " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
+ // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
+ // --include_current_bucket --proto
+ "";
+
+
+ private static final Logger sLogger = Logger.getLogger(LocalDrive.class.getName());
+
+ /** Usage: make statsd_localdrive && statsd_localdrive */
+ public static void main(String[] args) {
+ Utils.setUpLogger(sLogger, DEBUG);
+
+ if (args.length > 0) {
+ switch (args[0]) {
+ case "clear":
+ cmdClear(args);
+ return;
+ case "get-data":
+ cmdGetData(args);
+ return;
+ case "remove":
+ cmdRemove(args);
+ return;
+ case "update":
+ cmdUpdate(args);
+ return;
+ case "upload":
+ cmdUpload(args);
+ return;
+ }
+ }
+ printHelp();
+ }
+
+ private static void printHelp() {
+ sLogger.info(HELP_STRING);
+ }
+
+ // upload CONFIG_FILE [CONFIG_ID] [--binary]
+ private static boolean cmdUpload(String[] args) {
+ return updateConfig(args, true);
+ }
+
+ // update CONFIG_FILE [CONFIG_ID] [--binary]
+ private static boolean cmdUpdate(String[] args) {
+ return updateConfig(args, false);
+ }
+
+ private static boolean updateConfig(String[] args, boolean removeOldConfig) {
+ int argCount = args.length - 1; // Used up one for upload/update.
+
+ // Get CONFIG_FILE
+ if (argCount < 1) {
+ sLogger.severe("No config file provided.");
+ printHelp();
+ return false;
+ }
+ final String origConfigLocation = args[1];
+ if (!new File(origConfigLocation).exists()) {
+ sLogger.severe("Error - Cannot find the provided config file: " + origConfigLocation);
+ return false;
+ }
+ argCount--;
+
+ // Get --binary
+ boolean binary = contains(args, 2, BINARY_FLAG);
+ if (binary) argCount --;
+
+ // Get CONFIG_ID
+ long configId;
+ try {
+ configId = getConfigId(argCount < 1, args, 2);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("updateConfig with %s %d %b %b",
+ origConfigLocation, configId, binary, removeOldConfig));
+
+ // Remove the old config.
+ if (removeOldConfig) {
+ try {
+ Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
+ Utils.SHELL_UID, String.valueOf(configId));
+ Utils.getReportList(configId, true /* clearData */, sLogger);
+ } catch (InterruptedException | IOException e) {
+ sLogger.severe("Failed to remove config: " + e.getMessage());
+ return false;
+ }
+ }
+
+ // Upload the config.
+ String configLocation;
+ if (binary) {
+ configLocation = origConfigLocation;
+ } else {
+ StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+ try {
+ TextFormat.merge(new FileReader(origConfigLocation), builder);
+ } catch (IOException e) {
+ sLogger.severe("Failed to read config file " + origConfigLocation + ": "
+ + e.getMessage());
+ return false;
+ }
+
+ try {
+ File tempConfigFile = File.createTempFile("statsdconfig", ".config");
+ tempConfigFile.deleteOnExit();
+ Files.write(builder.build().toByteArray(), tempConfigFile);
+ configLocation = tempConfigFile.getAbsolutePath();
+ } catch (IOException e) {
+ sLogger.severe("Failed to write temp config file: " + e.getMessage());
+ return false;
+ }
+ }
+ String remotePath = "/data/local/tmp/statsdconfig.config";
+ try {
+ Utils.runCommand(null, sLogger, "adb", "push", configLocation, remotePath);
+ Utils.runCommand(null, sLogger, "adb", "shell", "cat", remotePath, "|",
+ Utils.CMD_UPDATE_CONFIG, Utils.SHELL_UID, String.valueOf(configId));
+ } catch (InterruptedException | IOException e) {
+ sLogger.severe("Failed to update config: " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ // get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]
+ private static boolean cmdGetData(String[] args) {
+ boolean binary = contains(args, 1, BINARY_FLAG);
+ boolean noUidMap = contains(args, 1, NO_UID_MAP_FLAG);
+ boolean clearData = contains(args, 1, CLEAR_DATA);
+
+ // Get CONFIG_ID
+ int argCount = args.length - 1; // Used up one for get-data.
+ if (binary) argCount--;
+ if (noUidMap) argCount--;
+ if (clearData) argCount--;
+ long configId;
+ try {
+ configId = getConfigId(argCount < 1, args, 1);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("cmdGetData with %d %b %b %b",
+ configId, clearData, binary, noUidMap));
+
+ // Get the StatsLog
+ // Even if the args request no modifications, we still parse it to make sure it's valid.
+ ConfigMetricsReportList reportList;
+ try {
+ reportList = Utils.getReportList(configId, clearData, sLogger);
+ } catch (IOException | InterruptedException e) {
+ sLogger.severe("Failed to get report list: " + e.getMessage());
+ return false;
+ }
+ if (noUidMap) {
+ ConfigMetricsReportList.Builder builder
+ = ConfigMetricsReportList.newBuilder(reportList);
+ // Clear the reports, then add them back without their UidMap.
+ builder.clearReports();
+ for (ConfigMetricsReport report : reportList.getReportsList()) {
+ builder.addReports(ConfigMetricsReport.newBuilder(report).clearUidMap());
+ }
+ reportList = builder.build();
+ }
+
+ if (!binary) {
+ sLogger.info(reportList.toString());
+ } else {
+ try {
+ System.out.write(reportList.toByteArray());
+ } catch (IOException e) {
+ sLogger.severe("Failed to output binary statslog proto: "
+ + e.getMessage());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // clear [CONFIG_ID]
+ private static boolean cmdClear(String[] args) {
+ // Get CONFIG_ID
+ long configId;
+ try {
+ configId = getConfigId(false, args, 1);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("cmdClear with %d", configId));
+
+ try {
+ Utils.getReportList(configId, true /* clearData */, sLogger);
+ } catch (IOException | InterruptedException e) {
+ sLogger.severe("Failed to get report list: " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ // remove [CONFIG_ID]
+ private static boolean cmdRemove(String[] args) {
+ // Get CONFIG_ID
+ long configId;
+ try {
+ configId = getConfigId(false, args, 1);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("cmdRemove with %d", configId));
+
+ try {
+ Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
+ Utils.SHELL_UID, String.valueOf(configId));
+ } catch (InterruptedException | IOException e) {
+ sLogger.severe("Failed to remove config: " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Searches through the array to see if it contains (precisely) the given value, starting
+ * at the given firstIdx.
+ */
+ private static boolean contains(String[] array, int firstIdx, String value) {
+ if (value == null) return false;
+ if (firstIdx < 0) return false;
+ for (int i = firstIdx; i < array.length; i++) {
+ if (value.equals(array[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the config id from args[idx], or returns DEFAULT_CONFIG_ID if args[idx] does not exist.
+ * If justUseDefault, overrides and just uses DEFAULT_CONFIG_ID instead.
+ */
+ private static long getConfigId(boolean justUseDefault, String[] args, int idx)
+ throws NumberFormatException {
+ if (justUseDefault || args.length <= idx || idx < 0) {
+ return DEFAULT_CONFIG_ID;
+ }
+ try {
+ return Long.valueOf(args[idx]);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Bad config id provided: " + args[idx]);
+ throw e;
+ }
+ }
+}
diff --git a/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
similarity index 68%
rename from cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
rename to cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index cc4e386..f7bd44a 100644
--- a/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.statsd.testdrive;
+package com.android.statsd.shelltools.testdrive;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
@@ -21,20 +21,15 @@
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.statsd.shelltools.Utils;
import com.google.common.io.Files;
import com.google.protobuf.TextFormat;
import com.google.protobuf.TextFormat.ParseException;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Formatter;
import java.util.logging.Level;
-import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class TestDrive {
@@ -42,10 +37,6 @@
public static final int PULL_ATOM_START = 10000;
public static final long ATOM_MATCHER_ID = 1234567;
- public static final String UPDATE_CONFIG_CMD = "cmd stats config update";
- public static final String DUMP_REPORT_CMD = "cmd stats dump-report";
- public static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
- public static final String CONFIG_UID = "2000"; // shell uid
public static final long CONFIG_ID = 54321;
private static boolean mIsPushedAtom = false;
@@ -53,6 +44,9 @@
private static final Logger logger = Logger.getLogger(TestDrive.class.getName());
public static void main(String[] args) {
+ TestDrive testDrive = new TestDrive();
+ Utils.setUpLogger(logger, false);
+
if (args.length != 1) {
logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>");
return;
@@ -70,12 +64,6 @@
}
mIsPushedAtom = atomId < PULL_ATOM_START;
- TestDrive testDrive = new TestDrive();
- TestDriveFormatter formatter = new TestDriveFormatter();
- ConsoleHandler handler = new ConsoleHandler();
- handler.setFormatter(formatter);
- logger.addHandler(handler);
- logger.setUseParentHandlers(false);
try {
StatsdConfig config = testDrive.createConfig(atomId);
@@ -109,55 +97,21 @@
configFile.deleteOnExit();
Files.write(config.toByteArray(), configFile);
String remotePath = "/data/local/tmp/" + configFile.getName();
- runCommand(null, "adb", "push", configFile.getAbsolutePath(), remotePath);
- runCommand(
- null, "adb", "shell", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
+ Utils.runCommand(null, logger, "adb", "push", configFile.getAbsolutePath(), remotePath);
+ Utils.runCommand(null, logger,
+ "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
String.valueOf(CONFIG_ID));
}
private void removeConfig() {
try {
- runCommand(null, "adb", "shell", REMOVE_CONFIG_CMD, String.valueOf(CONFIG_ID));
+ Utils.runCommand(null, logger,
+ "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
}
}
- // Runs a shell command. Output should go to outputFile. Returns error string.
- private String runCommand(File outputFile, String... commands)
- throws IOException, InterruptedException {
- // Run macro on target
- ProcessBuilder pb = new ProcessBuilder(commands);
- // pb.redirectErrorStream(true);
-
- if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
- pb.redirectOutput(outputFile);
- }
- Process process = pb.start();
-
- // capture any errors
- StringBuilder out = new StringBuilder();
- // Read output
- BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- String line = null, previous = null;
- while ((line = br.readLine()) != null) {
- if (!line.equals(previous)) {
- previous = line;
- out.append(line).append('\n');
- logger.fine(line);
- }
- }
-
- // Check result
- if (process.waitFor() == 0) {
- logger.fine("Success!");
- } else {
- // Abnormal termination: Log command parameters and output and throw ExecutionException
- logger.log(Level.SEVERE, out.toString());
- }
- return out.toString();
- }
-
private StatsdConfig createConfig(int atomId) {
try {
if (mIsPushedAtom) {
@@ -210,37 +164,8 @@
return builder;
}
- private ConfigMetricsReportList getReportList() throws Exception {
- try {
- File outputFile = File.createTempFile("statsdret", ".bin");
- outputFile.deleteOnExit();
- runCommand(
- outputFile,
- "adb",
- "shell",
- DUMP_REPORT_CMD,
- String.valueOf(CONFIG_ID),
- "--include_current_bucket",
- "--proto");
- ConfigMetricsReportList reportList =
- ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
- return reportList;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- logger.log(
- Level.SEVERE,
- "Failed to fetch and parse the statsd output report. "
- + "Perhaps there is not a valid statsd config for the requested "
- + "uid="
- + CONFIG_UID
- + ", id="
- + CONFIG_ID
- + ".");
- throw (e);
- }
- }
-
private void dumpMetrics() throws Exception {
- ConfigMetricsReportList reportList = getReportList();
+ ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, logger);
// We may get multiple reports. Take the last one.
ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
// Really should be only one metric.
@@ -294,9 +219,4 @@
+ "\n"
+ "hash_strings_in_metric_report: false";
- public static class TestDriveFormatter extends Formatter {
- public String format(LogRecord record) {
- return record.getMessage() + "\n";
- }
- }
}
diff --git a/cmds/statsd/tools/localtools/testdrive_manifest.txt b/cmds/statsd/tools/localtools/testdrive_manifest.txt
new file mode 100644
index 0000000..625ebfa
--- /dev/null
+++ b/cmds/statsd/tools/localtools/testdrive_manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.statsd.shelltools.testdrive.TestDrive
diff --git a/cmds/statsd/tools/statsd-testdrive/Android.bp b/cmds/statsd/tools/statsd-testdrive/Android.bp
deleted file mode 100644
index f566bc7..0000000
--- a/cmds/statsd/tools/statsd-testdrive/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-java_binary_host {
- name: "statsd_testdrive",
- manifest: "manifest.txt",
- srcs: [
- "src/**/*.java",
- ],
- static_libs: [
- "platformprotos",
- "guava",
- ],
-}
diff --git a/cmds/statsd/tools/statsd-testdrive/manifest.txt b/cmds/statsd/tools/statsd-testdrive/manifest.txt
deleted file mode 100644
index 0266d11..0000000
--- a/cmds/statsd/tools/statsd-testdrive/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-class: com.android.statsd.testdrive.TestDrive
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index f3bf6e7..a39f5e3 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,7 @@
private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
+ private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
"add-or-remove-call-companion-app";
@@ -68,6 +69,7 @@
"usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
+ "usage: telecom set-test-call-redirection-app <PACKAGE>\n" +
"usage: telecom set-test-call-screening-app <PACKAGE>\n" +
"usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
"usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
@@ -120,6 +122,9 @@
case COMMAND_REGISTER_PHONE_ACCOUNT:
runRegisterPhoneAccount();
break;
+ case COMMAND_SET_TEST_CALL_REDIRECTION_APP:
+ runSetTestCallRedirectionApp();
+ break;
case COMMAND_SET_TEST_CALL_SCREENING_APP:
runSetTestCallScreeningApp();
break;
@@ -189,6 +194,11 @@
System.out.println("Success - " + handle + " registered.");
}
+ private void runSetTestCallRedirectionApp() throws RemoteException {
+ final String packageName = nextArg();
+ mTelecomService.setTestDefaultCallRedirectionApp(packageName);
+ }
+
private void runSetTestCallScreeningApp() throws RemoteException {
final String packageName = nextArg();
mTelecomService.setTestDefaultCallScreeningApp(packageName);
diff --git a/config/hiddenapi-max-sdk-p-blacklist.txt b/config/hiddenapi-greylist-max-p.txt
similarity index 100%
rename from config/hiddenapi-max-sdk-p-blacklist.txt
rename to config/hiddenapi-greylist-max-p.txt
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-greylist.txt
similarity index 94%
rename from config/hiddenapi-light-greylist.txt
rename to config/hiddenapi-greylist.txt
index e10f729..bacb991 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -51,6 +51,7 @@
Landroid/app/backup/IFullBackupRestoreObserver$Stub;-><init>()V
Landroid/app/backup/IRestoreObserver$Stub;-><init>()V
Landroid/app/DownloadManager;->restartDownload([J)V
+Landroid/app/IActivityController$Stub;-><init>()V
Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
@@ -62,25 +63,35 @@
Landroid/app/IActivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager;
Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I
+Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
+Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
Landroid/app/IActivityManager;->checkPermission(Ljava/lang/String;II)I
+Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
Landroid/app/IActivityManager;->finishHeavyWeightApp()V
Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
Landroid/app/IActivityManager;->getAllStackInfos()Ljava/util/List;
Landroid/app/IActivityManager;->getConfiguration()Landroid/content/res/Configuration;
+Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
+Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
Landroid/app/IActivityManager;->getIntentForIntentSender(Landroid/content/IIntentSender;)Landroid/content/Intent;
Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender;
Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
Landroid/app/IActivityManager;->getLaunchedFromUid(Landroid/os/IBinder;)I
+Landroid/app/IActivityManager;->getLockTaskModeState()I
Landroid/app/IActivityManager;->getMemoryInfo(Landroid/app/ActivityManager$MemoryInfo;)V
Landroid/app/IActivityManager;->getPackageProcessState(Ljava/lang/String;Ljava/lang/String;)I
Landroid/app/IActivityManager;->getProcessLimit()I
+Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
Landroid/app/IActivityManager;->getProcessPss([I)[J
Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String;
+Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
+Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
Landroid/app/IActivityManager;->getServices(II)Ljava/util/List;
Landroid/app/IActivityManager;->getTaskBounds(I)Landroid/graphics/Rect;
Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
+Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
Landroid/app/IActivityManager;->handleApplicationStrictModeViolation(Landroid/os/IBinder;ILandroid/os/StrictMode$ViolationInfo;)V
Landroid/app/IActivityManager;->hang(Landroid/os/IBinder;Z)V
Landroid/app/IActivityManager;->isInLockTaskMode()Z
@@ -98,9 +109,11 @@
Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
Landroid/app/IActivityManager;->registerProcessObserver(Landroid/app/IProcessObserver;)V
Landroid/app/IActivityManager;->registerReceiver(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/IIntentReceiver;Landroid/content/IntentFilter;Ljava/lang/String;II)Landroid/content/Intent;
+Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
Landroid/app/IActivityManager;->registerUserSwitchObserver(Landroid/app/IUserSwitchObserver;Ljava/lang/String;)V
Landroid/app/IActivityManager;->removeContentProviderExternal(Ljava/lang/String;Landroid/os/IBinder;)V
Landroid/app/IActivityManager;->removeStack(I)V
+Landroid/app/IActivityManager;->removeTask(I)Z
Landroid/app/IActivityManager;->requestBugReport(I)V
Landroid/app/IActivityManager;->resizeDockedStack(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
Landroid/app/IActivityManager;->resizeStack(ILandroid/graphics/Rect;ZZZI)V
@@ -120,8 +133,12 @@
Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
Landroid/app/IActivityManager;->setTaskResizeable(II)V
Landroid/app/IActivityManager;->shutdown(I)Z
+Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
+Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
+Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
Landroid/app/IActivityManager;->startBinderTracking()Z
Landroid/app/IActivityManager;->startInstrumentation(Landroid/content/ComponentName;Ljava/lang/String;ILandroid/os/Bundle;Landroid/app/IInstrumentationWatcher;Landroid/app/IUiAutomationConnection;ILjava/lang/String;)Z
+Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
Landroid/app/IActivityManager;->startSystemLockTaskMode(I)V
Landroid/app/IActivityManager;->startUserInBackground(I)Z
Landroid/app/IActivityManager;->stopAppSwitches()V
@@ -144,12 +161,16 @@
Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
Landroid/app/IAlarmManager;->getNextAlarmClock(I)Landroid/app/AlarmManager$AlarmClockInfo;
Landroid/app/IAlarmManager;->set(Ljava/lang/String;IJJJILandroid/app/PendingIntent;Landroid/app/IAlarmListener;Ljava/lang/String;Landroid/os/WorkSource;Landroid/app/AlarmManager$AlarmClockInfo;)V
+Landroid/app/IAlarmManager;->setTime(J)Z
Landroid/app/IApplicationThread;->scheduleBindService(Landroid/os/IBinder;Landroid/content/Intent;ZI)V
Landroid/app/IApplicationThread;->scheduleCreateService(Landroid/os/IBinder;Landroid/content/pm/ServiceInfo;Landroid/content/res/CompatibilityInfo;I)V
Landroid/app/IApplicationThread;->scheduleStopService(Landroid/os/IBinder;)V
Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
Landroid/app/IApplicationThread;->scheduleUnbindService(Landroid/os/IBinder;Landroid/content/Intent;)V
Landroid/app/IAppTask;->getTaskInfo()Landroid/app/ActivityManager$RecentTaskInfo;
+Landroid/app/IAssistDataReceiver$Stub;-><init>()V
+Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
+Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z
Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher;
@@ -236,6 +257,7 @@
Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->isEnabled()Z
+Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
Landroid/bluetooth/IBluetoothA2dp$Stub;-><init>()V
Landroid/bluetooth/IBluetoothA2dp$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothA2dp;
Landroid/bluetooth/IBluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
@@ -266,6 +288,10 @@
Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V
Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
+Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
Landroid/content/ContentProviderProxy;->mRemote:Landroid/os/IBinder;
Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
@@ -312,11 +338,13 @@
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
+Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
@@ -325,6 +353,7 @@
Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
Landroid/content/pm/IPackageDeleteObserver2$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver2;
Landroid/content/pm/IPackageDeleteObserver2;->onPackageDeleted(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
Landroid/content/pm/IPackageInstaller;->uninstall(Landroid/content/pm/VersionedPackage;Ljava/lang/String;ILandroid/content/IntentSender;I)V
Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder;
@@ -364,11 +393,14 @@
Landroid/content/pm/IPackageManager;->clearPackagePreferredActivities(Ljava/lang/String;)V
Landroid/content/pm/IPackageManager;->currentToCanonicalPackageNames([Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->deleteApplicationCacheFiles(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
Landroid/content/pm/IPackageManager;->getApplicationEnabledSetting(Ljava/lang/String;I)I
+Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
Landroid/content/pm/IPackageManager;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->getBlockUninstallForUser(Ljava/lang/String;I)Z
Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I
Landroid/content/pm/IPackageManager;->getFlagsForUid(I)I
+Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
Landroid/content/pm/IPackageManager;->getInstalledApplications(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String;
@@ -376,6 +408,7 @@
Landroid/content/pm/IPackageManager;->getInstrumentationInfo(Landroid/content/ComponentName;I)Landroid/content/pm/InstrumentationInfo;
Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
Landroid/content/pm/IPackageManager;->getNameForUid(I)Ljava/lang/String;
+Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/IPackageManager;->getPackageInstaller()Landroid/content/pm/IPackageInstaller;
Landroid/content/pm/IPackageManager;->getPackagesForUid(I)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->getPackageUid(Ljava/lang/String;II)I
@@ -415,6 +448,7 @@
Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
+Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
Landroid/content/res/ConfigurationBoundResourceCache;-><init>()V
@@ -438,6 +472,7 @@
Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z
Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
+Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
@@ -447,7 +482,10 @@
Landroid/location/ICountryListener$Stub;-><init>()V
Landroid/location/IGeocodeProvider$Stub;-><init>()V
Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider;
+Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
+Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
Landroid/location/IGeofenceProvider$Stub;-><init>()V
+Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/location/ILocationListener$Stub;-><init>()V
@@ -460,6 +498,10 @@
Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
+Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
+Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
+Landroid/location/INetInitiatedListener$Stub;-><init>()V
+Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
@@ -486,6 +528,17 @@
Landroid/media/MediaScanner$MyMediaScannerClient;-><init>(Landroid/media/MediaScanner;)V
Landroid/media/projection/IMediaProjectionManager;->hasProjectionPermission(ILjava/lang/String;)Z
Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager;
+Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
+Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
+Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties;
Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
@@ -499,6 +552,7 @@
Landroid/net/IConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
Landroid/net/IConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
Landroid/net/IConnectivityManager;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
Landroid/net/IConnectivityManager;->getLastTetherError(Ljava/lang/String;)I
Landroid/net/IConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo;
Landroid/net/IConnectivityManager;->getTetherableIfaces()[Ljava/lang/String;
@@ -508,9 +562,12 @@
Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager;->reportInetCondition(II)V
Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
+Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
+Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
Landroid/net/INetworkPolicyListener$Stub;-><init>()V
Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
+Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
Landroid/net/INetworkPolicyManager;->getRestrictBackground()Z
Landroid/net/INetworkPolicyManager;->getUidPolicy(I)I
Landroid/net/INetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V
@@ -520,14 +577,19 @@
Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService;
Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
Landroid/net/INetworkStatsService;->forceUpdate()V
Landroid/net/INetworkStatsService;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats;
Landroid/net/INetworkStatsService;->getMobileIfaces()[Ljava/lang/String;
Landroid/net/INetworkStatsService;->openSession()Landroid/net/INetworkStatsSession;
Landroid/net/INetworkStatsService;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession;
Landroid/net/INetworkStatsSession;->close()V
+Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
+Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
Landroid/net/INetworkStatsSession;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats;
Landroid/net/INetworkStatsSession;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats;
+Landroid/net/InterfaceConfiguration;-><init>()V
+Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
Landroid/net/MobileLinkQualityInfo;-><init>()V
Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
Landroid/net/nsd/INsdManager;->getMessenger()Landroid/os/Messenger;
@@ -552,7 +614,12 @@
Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V
Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
+Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
+Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->result:Ljava/lang/Object;
+Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
@@ -569,18 +636,24 @@
Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_VOLTAGE:Ljava/lang/String;
Landroid/os/BatteryStats$Counter;-><init>()V
Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
+Landroid/os/BatteryStats$HistoryItem;-><init>()V
Landroid/os/BatteryStats$HistoryItem;->batteryHealth:B
+Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
Landroid/os/BatteryStats$HistoryItem;->batteryPlugType:B
Landroid/os/BatteryStats$HistoryItem;->batteryStatus:B
Landroid/os/BatteryStats$HistoryItem;->batteryVoltage:C
Landroid/os/BatteryStats$HistoryItem;->clear()V
+Landroid/os/BatteryStats$HistoryItem;->cmd:B
Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B
Landroid/os/BatteryStats$HistoryItem;->next:Landroid/os/BatteryStats$HistoryItem;
Landroid/os/BatteryStats$HistoryItem;->same(Landroid/os/BatteryStats$HistoryItem;)Z
Landroid/os/BatteryStats$HistoryItem;->setTo(JBLandroid/os/BatteryStats$HistoryItem;)V
Landroid/os/BatteryStats$HistoryItem;->setTo(Landroid/os/BatteryStats$HistoryItem;)V
Landroid/os/BatteryStats$HistoryItem;->states2:I
+Landroid/os/BatteryStats$HistoryItem;->states:I
+Landroid/os/BatteryStats$HistoryItem;->time:J
Landroid/os/BatteryStats$Timer;-><init>()V
+Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I
Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStarts(I)I
@@ -604,6 +677,8 @@
Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
Landroid/os/BatteryStats$Uid$Sensor;->GPS:I
Landroid/os/BatteryStats$Uid$Wakelock;-><init>()V
+Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
+Landroid/os/BatteryStats$Uid;-><init>()V
Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer;
Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
Landroid/os/BatteryStats$Uid;->getMobileRadioActiveTime(I)J
@@ -616,6 +691,7 @@
Landroid/os/BatteryStats$Uid;->getWakelockStats()Landroid/util/ArrayMap;
Landroid/os/BatteryStats$Uid;->getWifiBatchedScanTime(IJI)J
Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
+Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
Landroid/os/BatteryStats;-><init>()V
Landroid/os/BatteryStats;->computeBatteryRealtime(JI)J
@@ -627,6 +703,7 @@
Landroid/os/BatteryStats;->getGlobalWifiRunningTime(JI)J
Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J
Landroid/os/BatteryStats;->getNetworkActivityBytes(II)J
+Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
Landroid/os/BatteryStats;->getPhoneOnTime(JI)J
Landroid/os/BatteryStats;->getPhoneSignalStrengthTime(IJI)J
Landroid/os/BatteryStats;->getScreenBrightnessTime(IJI)J
@@ -640,6 +717,10 @@
Landroid/os/BatteryStats;->WAKE_TYPE_PARTIAL:I
Landroid/os/Binder;->execTransact(IJJI)Z
Landroid/os/Binder;->mObject:J
+Landroid/os/Broadcaster;-><init>()V
+Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
+Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
+Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
Landroid/os/Build;->getLong(Ljava/lang/String;)J
Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
@@ -713,6 +794,7 @@
Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File;
Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File;
Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
+Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
Landroid/os/Environment;->initForCurrentUser()V
Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
@@ -733,12 +815,15 @@
Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z
+Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
Landroid/os/Handler;-><init>(Z)V
Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
+Landroid/os/Handler;->getMain()Landroid/os/Handler;
Landroid/os/Handler;->getPostMessage(Ljava/lang/Runnable;Ljava/lang/Object;)Landroid/os/Message;
Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
Landroid/os/Handler;->mLooper:Landroid/os/Looper;
Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
+Landroid/os/HwBinder;->reportSyspropChanged()V
Landroid/os/HwParcel;-><init>(Z)V
Landroid/os/HwRemoteBinder;-><init>()V
Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -749,16 +834,26 @@
Landroid/os/IDeviceIdleController;->getAppIdTempWhitelist()[I
Landroid/os/IDeviceIdleController;->getFullPowerWhitelistExceptIdle()[Ljava/lang/String;
Landroid/os/INetworkManagementService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
+Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->disableNat(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->enableNat(Ljava/lang/String;Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->getInterfaceConfig(Ljava/lang/String;)Landroid/net/InterfaceConfiguration;
Landroid/os/INetworkManagementService;->getIpForwardingEnabled()Z
+Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
Landroid/os/INetworkManagementService;->isTetheringStarted()Z
Landroid/os/INetworkManagementService;->listTetheredInterfaces()[Ljava/lang/String;
+Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
+Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
+Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
Landroid/os/INetworkManagementService;->setIpForwardingEnabled(Z)V
+Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
Landroid/os/INetworkManagementService;->startTethering([Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->stopTethering()V
Landroid/os/INetworkManagementService;->tetherInterface(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
Landroid/os/INetworkManagementService;->untetherInterface(Ljava/lang/String;)V
Landroid/os/IPermissionController$Stub$Proxy;->checkPermission(Ljava/lang/String;II)Z
Landroid/os/IPermissionController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPermissionController;
@@ -767,12 +862,15 @@
Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I
Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
+Landroid/os/IPowerManager;->goToSleep(JII)V
Landroid/os/IPowerManager;->isInteractive()Z
Landroid/os/IPowerManager;->nap(J)V
+Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V
Landroid/os/IPowerManager;->userActivity(JII)V
Landroid/os/IPowerManager;->wakeUp(JLjava/lang/String;Ljava/lang/String;)V
Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem;
+Landroid/os/IRemoteCallback$Stub;-><init>()V
Landroid/os/IRemoteCallback;->sendResult(Landroid/os/Bundle;)V
Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
@@ -798,6 +896,7 @@
Landroid/os/Message;->markInUse()V
Landroid/os/Message;->next:Landroid/os/Message;
Landroid/os/Message;->recycleUnchecked()V
+Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
Landroid/os/Message;->target:Landroid/os/Handler;
Landroid/os/Message;->toString(J)Ljava/lang/String;
Landroid/os/Message;->when:J
@@ -817,13 +916,16 @@
Landroid/os/Parcel;->mNativePtr:J
Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V
Landroid/os/Parcel;->readArraySet(Ljava/lang/ClassLoader;)Landroid/util/ArraySet;
+Landroid/os/Parcel;->readBlob()[B
Landroid/os/Parcel;->readCharSequence()Ljava/lang/CharSequence;
Landroid/os/Parcel;->readCreator(Landroid/os/Parcelable$Creator;Ljava/lang/ClassLoader;)Landroid/os/Parcelable;
Landroid/os/Parcel;->readExceptionCode()I
Landroid/os/Parcel;->readParcelableCreator(Ljava/lang/ClassLoader;)Landroid/os/Parcelable$Creator;
Landroid/os/Parcel;->readRawFileDescriptor()Ljava/io/FileDescriptor;
+Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
Landroid/os/Parcel;->writeArraySet(Landroid/util/ArraySet;)V
+Landroid/os/Parcel;->writeBlob([B)V
Landroid/os/Parcel;->writeCharSequence(Ljava/lang/CharSequence;)V
Landroid/os/Parcel;->writeParcelableCreator(Landroid/os/Parcelable;)V
Landroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V
@@ -890,15 +992,27 @@
Landroid/os/Process;->VPN_UID:I
Landroid/os/Process;->WIFI_UID:I
Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z
+Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/Registrant;->clear()V
Landroid/os/Registrant;->getHandler()Landroid/os/Handler;
Landroid/os/Registrant;->messageForRegistrant()Landroid/os/Message;
+Landroid/os/Registrant;->notifyRegistrant()V
+Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
Landroid/os/Registrant;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;-><init>()V
Landroid/os/RegistrantList;->add(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
+Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
Landroid/os/RegistrantList;->get(I)Ljava/lang/Object;
+Landroid/os/RegistrantList;->notifyRegistrants()V
+Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
Landroid/os/RegistrantList;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
+Landroid/os/RegistrantList;->removeCleared()V
Landroid/os/RegistrantList;->size()I
Landroid/os/RemoteCallback;->mHandler:Landroid/os/Handler;
Landroid/os/RemoteCallbackList;->mCallbacks:Landroid/util/ArrayMap;
+Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
Landroid/os/SELinux;->checkSELinuxAccess(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
Landroid/os/SELinux;->getContext()Ljava/lang/String;
Landroid/os/SELinux;->getFileContext(Ljava/lang/String;)Ljava/lang/String;
@@ -920,6 +1034,7 @@
Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder;
Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V
+Landroid/os/ServiceSpecificException;->errorCode:I
Landroid/os/SharedMemory;->getFd()I
Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs;
@@ -927,6 +1042,7 @@
Landroid/os/storage/IObbActionListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IObbActionListener;
Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
+Landroid/os/storage/StorageEventListener;-><init>()V
Landroid/os/StrictMode$Span;->finish()V
Landroid/os/StrictMode$ThreadPolicy;->mask:I
Landroid/os/StrictMode$VmPolicy$Builder;->mMask:I
@@ -954,8 +1070,12 @@
Landroid/os/SystemProperties;->native_get_long(Ljava/lang/String;J)J
Landroid/os/SystemProperties;->native_set(Ljava/lang/String;Ljava/lang/String;)V
Landroid/os/SystemProperties;->PROP_NAME_MAX:I
+Landroid/os/SystemProperties;->reportSyspropChanged()V
Landroid/os/SystemProperties;->sChangeCallbacks:Ljava/util/ArrayList;
Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/SystemService;->start(Ljava/lang/String;)V
+Landroid/os/SystemService;->stop(Ljava/lang/String;)V
+Landroid/os/SystemVibrator;-><init>()V
Landroid/os/SystemVibrator;-><init>(Landroid/content/Context;)V
Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
@@ -994,6 +1114,7 @@
Landroid/os/UserHandle;->getUid(II)I
Landroid/os/UserHandle;->getUserId(I)I
Landroid/os/UserHandle;->isIsolated(I)Z
+Landroid/os/UserHandle;->isSameApp(II)Z
Landroid/os/UserHandle;->mHandle:I
Landroid/os/UserHandle;->MU_ENABLED:Z
Landroid/os/UserHandle;->OWNER:Landroid/os/UserHandle;
@@ -1023,6 +1144,7 @@
Landroid/os/UserManager;->getUserStartRealtime()J
Landroid/os/UserManager;->getUserUnlockRealtime()J
Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z
Landroid/os/UserManager;->isGuestUser(I)Z
Landroid/os/UserManager;->isLinkedUser()Z
@@ -1072,6 +1194,7 @@
Landroid/R$styleable;->CalendarView_weekDayTextAppearance:I
Landroid/R$styleable;->CalendarView_weekNumberColor:I
Landroid/R$styleable;->CalendarView_weekSeparatorLineColor:I
+Landroid/R$styleable;->CheckBoxPreference:[I
Landroid/R$styleable;->CheckedTextView:[I
Landroid/R$styleable;->CheckedTextView_checkMark:I
Landroid/R$styleable;->CompoundButton:[I
@@ -1382,7 +1505,18 @@
Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
+Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
+Landroid/telephony/ims/compat/ImsService;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
Landroid/telephony/JapanesePhoneNumberFormatter;->format(Landroid/text/Editable;)V
+Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
+Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
+Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
Landroid/telephony/SmsCbCmasInfo;->getCategory()I
Landroid/telephony/SmsCbCmasInfo;->getCertainty()I
Landroid/telephony/SmsCbCmasInfo;->getMessageClass()I
@@ -1424,10 +1558,21 @@
Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;-><init>()V
Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
+Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
Landroid/view/IDockedStackListener$Stub;-><init>()V
Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats;
+Landroid/view/IRecentsAnimationController;->finish(Z)V
+Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V
+Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
+Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
+Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
Landroid/view/IRotationWatcher$Stub;-><init>()V
Landroid/view/IWindow$Stub;-><init>()V
Landroid/view/IWindow$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindow;
@@ -1439,6 +1584,9 @@
Landroid/view/IWindowManager$Stub$Proxy;->watchRotation(Landroid/view/IRotationWatcher;I)I
Landroid/view/IWindowManager$Stub;-><init>()V
Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
+Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
+Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
+Landroid/view/IWindowManager;->endProlongedAnimations()V
Landroid/view/IWindowManager;->executeAppTransition()V
Landroid/view/IWindowManager;->freezeRotation(I)V
Landroid/view/IWindowManager;->getAnimationScale(I)F
@@ -1447,16 +1595,20 @@
Landroid/view/IWindowManager;->getDockedStackSide()I
Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I
Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
+Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
Landroid/view/IWindowManager;->hasNavigationBar(I)Z
Landroid/view/IWindowManager;->isKeyguardLocked()Z
Landroid/view/IWindowManager;->isKeyguardSecure()Z
Landroid/view/IWindowManager;->isSafeModeEnabled()Z
Landroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
Landroid/view/IWindowManager;->registerDockedStackListener(Landroid/view/IDockedStackListener;)V
Landroid/view/IWindowManager;->removeRotationWatcher(Landroid/view/IRotationWatcher;)V
Landroid/view/IWindowManager;->setAnimationScale(IF)V
Landroid/view/IWindowManager;->setAnimationScales([F)V
Landroid/view/IWindowManager;->setInTouchMode(Z)V
+Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
Landroid/view/IWindowManager;->setShelfHeight(ZI)V
Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
@@ -1487,15 +1639,12 @@
Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V
Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V
Landroid/widget/ScrollBarDrawable;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
Lcom/android/ims/ImsCall;->isMultiparty()Z
Lcom/android/ims/ImsCall;->reject(I)V
Lcom/android/ims/ImsCall;->terminate(I)V
Lcom/android/ims/ImsConfigListener$Stub;-><init>()V
+Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
Lcom/android/ims/ImsEcbm;->exitEmergencyCallbackMode()V
Lcom/android/ims/ImsManager;->getConfigInterface()Lcom/android/ims/ImsConfig;
Lcom/android/ims/ImsManager;->getInstance(Landroid/content/Context;I)Lcom/android/ims/ImsManager;
@@ -1505,12 +1654,48 @@
Lcom/android/ims/ImsUtInterface;->queryCallForward(ILjava/lang/String;Landroid/os/Message;)V
Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V
Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession;
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V
Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnected()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
Lcom/android/ims/internal/IImsService$Stub;-><init>()V
Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService;
Lcom/android/ims/internal/IImsUt$Stub;-><init>()V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
Lcom/android/ims/internal/IImsVideoCallCallback;->changeCallDataUsage(J)V
Lcom/android/ims/internal/IImsVideoCallCallback;->changeCameraCapabilities(Landroid/telecom/VideoProfile$CameraCapabilities;)V
Lcom/android/ims/internal/IImsVideoCallCallback;->changePeerDimensions(II)V
@@ -1521,7 +1706,52 @@
Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
Lcom/android/ims/internal/ImsVideoCallProviderWrapper;-><init>(Lcom/android/ims/internal/IImsVideoCallProvider;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
+Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
+Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
Lcom/android/internal/app/AlertActivity;-><init>()V
Lcom/android/internal/app/AlertActivity;->mAlert:Lcom/android/internal/app/AlertController;
Lcom/android/internal/app/AlertActivity;->mAlertParams:Lcom/android/internal/app/AlertController$AlertParams;
@@ -1552,6 +1782,7 @@
Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startOperation:I
Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startWatchingMode:I
Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_stopWatchingMode:I
+Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
Lcom/android/internal/app/IAppOpsService;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
Lcom/android/internal/app/IAppOpsService;->getPackagesForOps([I)Ljava/util/List;
Lcom/android/internal/app/IAppOpsService;->resetAllModes(ILjava/lang/String;)V
@@ -1599,6 +1830,7 @@
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
+Lcom/android/internal/content/PackageMonitor;-><init>()V
Lcom/android/internal/database/SortCursor;-><init>([Landroid/database/Cursor;Ljava/lang/String;)V
Lcom/android/internal/database/SortCursor;->mCursor:Landroid/database/Cursor;
Lcom/android/internal/database/SortCursor;->mCursors:[Landroid/database/Cursor;
@@ -1612,6 +1844,17 @@
Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;)V
Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
+Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
+Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)V
+Lcom/android/internal/location/ILocationProvider;->setLocationProviderManager(Lcom/android/internal/location/ILocationProviderManager;)V
+Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
+Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
+Lcom/android/internal/location/ILocationProviderManager;->onReportLocation(Landroid/location/Location;)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetEnabled(Z)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetProperties(Lcom/android/internal/location/ProviderProperties;)V
Lcom/android/internal/logging/MetricsLogger;-><init>()V
Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
Lcom/android/internal/net/VpnConfig;-><init>()V
@@ -1981,6 +2224,7 @@
Lcom/android/internal/R$styleable;->MenuGroup:[I
Lcom/android/internal/R$styleable;->MenuItem:[I
Lcom/android/internal/R$styleable;->MenuView:[I
+Lcom/android/internal/R$styleable;->NumberPicker:[I
Lcom/android/internal/R$styleable;->PopupWindow:[I
Lcom/android/internal/R$styleable;->PopupWindow_popupAnimationStyle:I
Lcom/android/internal/R$styleable;->PopupWindow_popupBackground:I
@@ -2106,6 +2350,7 @@
Lcom/android/internal/R$styleable;->TextView_typeface:I
Lcom/android/internal/R$styleable;->TextView_width:I
Lcom/android/internal/R$styleable;->Theme:[I
+Lcom/android/internal/R$styleable;->TwoLineListItem:[I
Lcom/android/internal/R$styleable;->View:[I
Lcom/android/internal/R$styleable;->ViewAnimator:[I
Lcom/android/internal/R$styleable;->ViewFlipper:[I
@@ -2658,8 +2903,6 @@
Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(ZLjava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpConnection(ZLcom/android/internal/telephony/dataconnection/ApnContext;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->createAllApnList()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->getActiveApnTypes()[Ljava/lang/String;
Lcom/android/internal/telephony/dataconnection/DcTracker;->getOverallState()Lcom/android/internal/telephony/DctConstants$State;
@@ -2690,7 +2933,6 @@
Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyOffApnsOfAvailability(Ljava/lang/String;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentDataStallAlarm(Landroid/content/Intent;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentProvisioningApnAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onCleanUpAllConnections(Ljava/lang/String;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubIdChanged()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->onSetUserDataEnabled(Z)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
@@ -2699,7 +2941,6 @@
Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->setInitialAttachApn()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setInternalDataEnabled(ZLandroid/os/Message;)Z
Lcom/android/internal/telephony/dataconnection/DcTracker;->setPreferredApn(I)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->setRadio(Z)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->setupDataOnConnectableApns(Ljava/lang/String;)V
@@ -3126,6 +3367,7 @@
Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I
Lcom/android/internal/telephony/ITelephony;->getCallState()I
Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
+Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
Lcom/android/internal/telephony/ITelephony;->getDataState()I
Lcom/android/internal/telephony/ITelephony;->getNetworkType()I
Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
deleted file mode 100644
index a21fa30..0000000
--- a/config/hiddenapi-vendor-list.txt
+++ /dev/null
@@ -1,247 +0,0 @@
-Landroid/app/IActivityController$Stub;-><init>()V
-Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
-Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
-Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
-Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
-Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
-Landroid/app/IActivityManager;->getLockTaskModeState()I
-Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
-Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
-Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
-Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
-Landroid/app/IActivityManager;->removeTask(I)Z
-Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
-Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
-Landroid/app/IAlarmManager;->setTime(J)Z
-Landroid/app/IAssistDataReceiver$Stub;-><init>()V
-Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
-Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
-Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
-Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
-Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
-Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
-Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
-Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
-Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
-Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
-Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
-Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
-Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
-Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
-Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
-Landroid/location/INetInitiatedListener$Stub;-><init>()V
-Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
-Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
-Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
-Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
-Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
-Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
-Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
-Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
-Landroid/net/InterfaceConfiguration;-><init>()V
-Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
-Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
-Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
-Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
-Landroid/os/AsyncResult;->result:Ljava/lang/Object;
-Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
-Landroid/os/BatteryStats$HistoryItem;-><init>()V
-Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
-Landroid/os/BatteryStats$HistoryItem;->cmd:B
-Landroid/os/BatteryStats$HistoryItem;->states:I
-Landroid/os/BatteryStats$HistoryItem;->time:J
-Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
-Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
-Landroid/os/BatteryStats$Uid;-><init>()V
-Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
-Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
-Landroid/os/Broadcaster;-><init>()V
-Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
-Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
-Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
-Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
-Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
-Landroid/os/Handler;->getMain()Landroid/os/Handler;
-Landroid/os/HwBinder;->reportSyspropChanged()V
-Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
-Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
-Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
-Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
-Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
-Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/IPowerManager;->goToSleep(JII)V
-Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
-Landroid/os/IRemoteCallback$Stub;-><init>()V
-Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
-Landroid/os/Parcel;->readBlob()[B
-Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
-Landroid/os/Parcel;->writeBlob([B)V
-Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/Registrant;->clear()V
-Landroid/os/Registrant;->notifyRegistrant()V
-Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;-><init>()V
-Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
-Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/RegistrantList;->notifyRegistrants()V
-Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
-Landroid/os/RegistrantList;->removeCleared()V
-Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
-Landroid/os/ServiceSpecificException;->errorCode:I
-Landroid/os/storage/StorageEventListener;-><init>()V
-Landroid/os/SystemProperties;->reportSyspropChanged()V
-Landroid/os/SystemService;->start(Ljava/lang/String;)V
-Landroid/os/SystemService;->stop(Ljava/lang/String;)V
-Landroid/os/SystemVibrator;-><init>()V
-Landroid/os/UserHandle;->isSameApp(II)Z
-Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
-Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
-Landroid/telephony/ims/compat/ImsService;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
-Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
-Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
-Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
-Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
-Landroid/view/IRecentsAnimationController;->finish(Z)V
-Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
-Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
-Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
-Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
-Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
-Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
-Landroid/view/IWindowManager;->endProlongedAnimations()V
-Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
-Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
-Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
-Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
-Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
-Lcom/android/internal/content/PackageMonitor;-><init>()V
-Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
-Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
-Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
-Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
-Lcom/android/internal/R$styleable;->NumberPicker:[I
-Lcom/android/internal/R$styleable;->TwoLineListItem:[I
-Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index 28145a0..ac3daaf 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -18,29 +18,48 @@
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.CLASS;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- * Indicates that a class member, that is not part of the SDK, is used by apps.
- * Since the member is not part of the SDK, such use is not supported.
+ * Indicates that this non-SDK interface is used by apps. A non-SDK interface is a
+ * class member (field or method) that is not part of the public SDK. Since the
+ * member is not part of the SDK, usage by apps is not supported.
*
- * <p>This annotation acts as a heads up that changing a given method or field
+ * <h2>If you are an Android App developer</h2>
+ *
+ * This annotation indicates that you may be able to access the member, but that
+ * this access is discouraged and not supported by Android. If there is a value
+ * for {@link #maxTargetSdk()} on the annotation, access will be restricted based
+ * on the {@code targetSdkVersion} value set in your manifest.
+ *
+ * <p>Fields and methods annotated with this are likely to be restricted, changed
+ * or removed in future Android releases. If you rely on these members for
+ * functionality that is not otherwise supported by Android, consider filing a
+ * <a href="http://g.co/dev/appcompat">feature request</a>.
+ *
+ * <h2>If you are an Android OS developer</h2>
+ *
+ * This annotation acts as a heads up that changing a given method or field
* may affect apps, potentially breaking them when the next Android version is
* released. In some cases, for members that are heavily used, this annotation
* may imply restrictions on changes to the member.
*
* <p>This annotation also results in access to the member being permitted by the
- * runtime, with a warning being generated in debug builds.
+ * runtime, with a warning being generated in debug builds. Which apps can access
+ * the member is determined by the value of {@link #maxTargetSdk()}.
*
* <p>For more details, see go/UnsupportedAppUsage.
*
* {@hide}
*/
@Retention(CLASS)
-@Target({CONSTRUCTOR, METHOD, FIELD})
+@Target({CONSTRUCTOR, METHOD, FIELD, TYPE})
+@Repeatable(UnsupportedAppUsage.Container.class)
public @interface UnsupportedAppUsage {
/**
@@ -90,4 +109,27 @@
* @return A dex API signature.
*/
String expectedSignature() default "";
+
+ /**
+ * The signature of an implicit (not present in the source) member that forms part of the
+ * hiddenapi.
+ *
+ * <p>Allows access to non-SDK API elements that are not represented in the input source to be
+ * managed.
+ *
+ * <p>This must only be used when applying the annotation to a type, using it in any other
+ * situation is an error.
+ *
+ * @return A dex API signature.
+ */
+ String implicitMember() default "";
+
+ /**
+ * Container for {@link UnsupportedAppUsage} that allows it to be applied repeatedly to types.
+ */
+ @Retention(CLASS)
+ @Target(TYPE)
+ @interface Container {
+ UnsupportedAppUsage[] value();
+ }
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f3f065a..5e445d14 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -121,8 +121,8 @@
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.autofill.AutofillPopupWindow;
import android.view.autofill.IAutofillWindowPresenter;
-import android.view.intelligence.ContentCaptureEvent;
-import android.view.intelligence.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -1723,15 +1723,7 @@
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
- // On Activity rotation situation (mRestoredFromBundle is true),
- // we should not call on AutofillManager in onResume()
- // since the next Layout pass will do that.
- // However, there are both cases where Activity#getCurrentFocus()
- // will return null (window not preserved) and not null (window IS
- // preserved), so we need to explicitly check for mRestoredFromBundle
- // here.
- if (!mRestoredFromBundle && focus != null
- && focus.canNotifyAutofillEnterExitEvent()) {
+ if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
// TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
// testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
// window visibility after recreation is INVISIBLE in onResume() and next frame
@@ -6763,8 +6755,8 @@
case "--autofill":
dumpAutofillManager(prefix, writer);
return;
- case "--intelligence":
- dumpIntelligenceManager(prefix, writer);
+ case "--contentcapture":
+ dumpContentCaptureManager(prefix, writer);
return;
}
}
@@ -6796,7 +6788,7 @@
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
dumpAutofillManager(prefix, writer);
- dumpIntelligenceManager(prefix, writer);
+ dumpContentCaptureManager(prefix, writer);
ResourcesManager.getInstance().dump(prefix, writer);
}
@@ -6812,12 +6804,12 @@
}
}
- void dumpIntelligenceManager(String prefix, PrintWriter writer) {
- final ContentCaptureManager im = getContentCaptureManager();
- if (im != null) {
- im.dump(prefix, writer);
+ void dumpContentCaptureManager(String prefix, PrintWriter writer) {
+ final ContentCaptureManager cm = getContentCaptureManager();
+ if (cm != null) {
+ cm.dump(prefix, writer);
} else {
- writer.print(prefix); writer.println("No IntelligenceManager");
+ writer.print(prefix); writer.println("No ContentCaptureManager");
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1cf042f..84c7785 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3676,6 +3676,18 @@
}
/**
+ * Returns whether switching to provided user was successful.
+ *
+ * @param user the user to switch to.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean switchUser(UserHandle user) {
+ return switchUser(user.getIdentifier());
+ }
+
+ /**
* Logs out current current foreground user by switching to the system user and stopping the
* user being switched from.
* @hide
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index af3da0c..f928501 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -196,8 +196,26 @@
public abstract void updateOomAdj();
public abstract void updateCpuStats();
- public abstract void updateUsageStats(
+
+ /**
+ * Update battery stats on activity usage.
+ * @param activity
+ * @param uid
+ * @param userId
+ * @param started
+ */
+ public abstract void updateBatteryStats(
ComponentName activity, int uid, int userId, boolean resumed);
+
+ /**
+ * Update UsageStats of the activity.
+ * @param activity
+ * @param userId
+ * @param event
+ * @param appToken ActivityRecord's appToken.
+ */
+ public abstract void updateActivityUsageStats(
+ ComponentName activity, int userId, int event, IBinder appToken);
public abstract void updateForegroundTimeIfOnBattery(
String packageName, int uid, long cpuTimeDiff);
public abstract void sendForegroundProfileChanged(int userId);
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fdf7c8..4d8c856 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -303,9 +303,7 @@
* @param stackId Id of stack to move the top activity to pinned stack.
* @param bounds Bounds to use for pinned stack.
* @return True if the top activity of stack was successfully moved to the pinned stack.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
try {
@@ -318,9 +316,7 @@
/**
* Start to enter lock task mode for given task by system(UI).
* @param taskId Id of task to lock.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void startSystemLockTaskMode(int taskId) {
try {
@@ -332,9 +328,7 @@
/**
* Stop lock task mode by system(UI).
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void stopSystemLockTaskMode() {
try {
@@ -349,9 +343,7 @@
* @param taskId Id of the task to move.
* @param stackId Id of the stack for task moving.
* @param toTop Whether the given task should shown to top of stack.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
try {
@@ -366,9 +358,7 @@
* @param stackId Id of the stack to resize.
* @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
* @param animate Whether we should play an animation for resizing stack.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void resizeStack(int stackId, Rect bounds, boolean animate) {
try {
@@ -383,9 +373,7 @@
* Resize task to given bounds.
* @param taskId Id of task to resize.
* @param bounds Bounds to resize task.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void resizeTask(int taskId, Rect bounds) {
try {
@@ -399,9 +387,7 @@
* Resize docked stack & its task to given stack & task bounds.
* @param stackBounds Bounds to resize stack.
* @param taskBounds Bounds to resize task.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void resizeDockedStack(Rect stackBounds, Rect taskBounds) {
try {
@@ -413,9 +399,7 @@
/**
* List all activity stacks information.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public String listAllStacks() {
final List<ActivityManager.StackInfo> stacks;
@@ -438,7 +422,6 @@
* Clears launch params for the given package.
* @param packageNames the names of the packages of which the launch params are to be cleared
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void clearLaunchParamsForPackages(List<String> packageNames) {
try {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 41166dd..fe9b1ff 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -162,7 +162,6 @@
import com.android.org.conscrypt.TrustedCertificateStore;
import com.android.server.am.MemInfoDumpProto;
-import dalvik.system.BaseDexClassLoader;
import dalvik.system.CloseGuard;
import dalvik.system.VMDebug;
import dalvik.system.VMRuntime;
@@ -2282,6 +2281,15 @@
}
}
+ /**
+ * Create the context instance base on system resources & display information which used for UI.
+ * @param displayId The ID of the display where the UI is shown.
+ * @see ContextImpl#createSystemUiContext(ContextImpl, int)
+ */
+ public ContextImpl createSystemUiContext(int displayId) {
+ return ContextImpl.createSystemUiContext(getSystemUiContext(), displayId);
+ }
+
public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
synchronized (this) {
getSystemContext().installSystemApplicationInfo(info, classLoader);
@@ -5948,16 +5956,6 @@
HardwareRenderer.setIsolatedProcess(true);
}
- // If we use profiles, setup the dex reporter to notify package manager
- // of any relevant dex loads. The idle maintenance job will use the information
- // reported to optimize the loaded dex files.
- // Note that we only need one global reporter per app.
- // Make sure we do this before calling onCreate so that we can capture the
- // complete application startup.
- if (SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
- BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
- }
-
// Install the Network Security Config Provider. This must happen before the application
// code is loaded to prevent issues with instances of TLS objects being created before
// the provider is installed.
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 446d98e..2c435a2 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -31,7 +31,6 @@
import android.util.Log;
import android.view.IWindowManager;
import android.view.InputDevice;
-import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -291,9 +290,14 @@
return super.onGenericMotionEvent(event);
}
- private boolean injectInputEvent(InputEvent event) {
+ private boolean injectInputEvent(MotionEvent event) {
if (mInputForwarder != null) {
try {
+ // The touch event that the ActivityView gets is in View space, but the event needs
+ // to get forwarded in screen space. This offsets the touch event by the location
+ // the ActivityView is on screen and sends it to the input forwarder.
+ getLocationOnScreen(mLocationOnScreen);
+ event.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
return mInputForwarder.forwardEvent(event);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3069be6..6905cb5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -476,9 +476,11 @@
public static final int OP_READ_MEDIA_IMAGES = 85;
/** @hide Write media of image type. */
public static final int OP_WRITE_MEDIA_IMAGES = 86;
+ /** @hide Has a legacy (non-isolated) view of storage. */
+ public static final int OP_LEGACY_STORAGE = 87;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 87;
+ public static final int _NUM_OP = 88;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -745,6 +747,8 @@
public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
/** @hide Write media of image type. */
public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+ /** @hide Has a legacy (non-isolated) view of storage. */
+ public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
@@ -903,6 +907,7 @@
OP_WRITE_MEDIA_VIDEO, // WRITE_MEDIA_VIDEO
OP_READ_MEDIA_IMAGES, // READ_MEDIA_IMAGES
OP_WRITE_MEDIA_IMAGES, // WRITE_MEDIA_IMAGES
+ OP_LEGACY_STORAGE, // LEGACY_STORAGE
};
/**
@@ -996,6 +1001,7 @@
OPSTR_WRITE_MEDIA_VIDEO,
OPSTR_READ_MEDIA_IMAGES,
OPSTR_WRITE_MEDIA_IMAGES,
+ OPSTR_LEGACY_STORAGE,
};
/**
@@ -1090,6 +1096,7 @@
"WRITE_MEDIA_VIDEO",
"READ_MEDIA_IMAGES",
"WRITE_MEDIA_IMAGES",
+ "LEGACY_STORAGE",
};
/**
@@ -1185,6 +1192,7 @@
null, // no permission for OP_WRITE_MEDIA_VIDEO
Manifest.permission.READ_MEDIA_IMAGES,
null, // no permission for OP_WRITE_MEDIA_IMAGES
+ null, // no permission for OP_LEGACY_STORAGE
};
/**
@@ -1280,6 +1288,7 @@
null, // WRITE_MEDIA_VIDEO
null, // READ_MEDIA_IMAGES
null, // WRITE_MEDIA_IMAGES
+ null, // LEGACY_STORAGE
};
/**
@@ -1374,6 +1383,7 @@
false, // WRITE_MEDIA_VIDEO
false, // READ_MEDIA_IMAGES
false, // WRITE_MEDIA_IMAGES
+ false, // LEGACY_STORAGE
};
/**
@@ -1467,6 +1477,7 @@
AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
+ AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
};
/**
@@ -1564,6 +1575,7 @@
false, // WRITE_MEDIA_VIDEO
false, // READ_MEDIA_IMAGES
false, // WRITE_MEDIA_IMAGES
+ false, // LEGACY_STORAGE
};
/**
@@ -2490,30 +2502,6 @@
}
/**
- * Retrieve current operation state for all applications.
- *
- * @param ops The set of operations you are interested in, or null if you want all of them.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
- @SystemApi
- public List<AppOpsManager.PackageOps> getPackagesForOpStrs(String[] ops) {
- if (ops == null) {
- return getPackagesForOps(null);
- }
- final int[] opCodes = new int[ops.length];
- for (int i = 0; i < ops.length; ++i) {
- final Integer opCode = sOpStrToOp.get(ops[i]);
- if (opCode == null) {
- opCodes[i] = OP_NONE;
- } else {
- opCodes[i] = opCode;
- }
- }
- return getPackagesForOps(opCodes);
- }
-
- /**
* Retrieve current operation state for one application.
*
* @param uid The uid of the application of interest.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7312b2c..67d9ad6e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2974,6 +2974,15 @@
}
@Override
+ public String getWellbeingPackageName() {
+ try {
+ return mPM.getWellbeingPackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public boolean isPackageStateProtected(String packageName, int userId) {
try {
return mPM.isPackageStateProtected(packageName, userId);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6f0b6c8..92cdb20 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1731,8 +1731,11 @@
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
- IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
- getOuterContext(), conn);
+ IServiceConnection sd = mPackageInfo.lookupServiceDispatcher(conn, getOuterContext());
+ if (sd == null) {
+ throw new IllegalArgumentException("ServiceConnection not currently bound: "
+ + conn);
+ }
try {
ActivityManager.getService().updateServiceGroup(sd, group, importance);
} catch (RemoteException e) {
@@ -2404,16 +2407,28 @@
/**
* System Context to be used for UI. This Context has resources that can be themed.
* Make sure that the created system UI context shares the same LoadedApk as the system context.
+ * @param systemContext The system context which created by
+ * {@link #createSystemContext(ActivityThread)}.
+ * @param displayId The ID of the display where the UI is shown.
*/
- static ContextImpl createSystemUiContext(ContextImpl systemContext) {
+ static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
final LoadedApk packageInfo = systemContext.mPackageInfo;
ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
null, null, 0, null);
- context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
+ context.setResources(createResources(null, packageInfo, null, displayId, null,
packageInfo.getCompatibilityInfo()));
+ context.updateDisplay(displayId);
return context;
}
+ /**
+ * The overloaded method of {@link #createSystemUiContext(ContextImpl, int)}.
+ * Uses {@Code Display.DEFAULT_DISPLAY} as the target display.
+ */
+ static ContextImpl createSystemUiContext(ContextImpl systemContext) {
+ return createSystemUiContext(systemContext, Display.DEFAULT_DISPLAY);
+ }
+
@UnsupportedAppUsage
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e83bcd0..88fb025 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -96,6 +96,7 @@
String callingPackage);
void unregisterUidObserver(in IUidObserver observer);
boolean isUidActive(int uid, String callingPackage);
+ int getUidProcessState(int uid, in String callingPackage);
// =============== End of transactions used on native side as well ============================
// Special low-level communication with activity manager.
@@ -379,8 +380,6 @@
void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
int getPackageProcessState(in String packageName, in String callingPackage);
void updateDeviceOwner(in String packageName);
- int getUidProcessState(int uid, in String callingPackage);
-
// Start of N transactions
// Start Binder transaction tracking for all applications.
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index ce88809..e116d98 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -43,8 +43,6 @@
*/
void onUidIdle(int uid, boolean disabled);
- // =============== End of transactions used on native side as well ============================
-
/**
* General report of a state change of an uid.
*
@@ -55,6 +53,8 @@
*/
void onUidStateChanged(int uid, int procState, long procStateSeq);
+ // =============== End of transactions used on native side as well ============================
+
/**
* Report when the cached state of a uid has changed.
* If true, a uid has become cached -- that is, it has some active processes that are
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 3f10754..d46dbed 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -57,6 +57,7 @@
import com.android.internal.util.ArrayUtils;
+import dalvik.system.BaseDexClassLoader;
import dalvik.system.VMRuntime;
import java.io.File;
@@ -696,6 +697,23 @@
return loaders;
}
+ private StrictMode.ThreadPolicy allowThreadDiskReads() {
+ if (mActivityThread == null) {
+ // When LoadedApk is used without an ActivityThread (usually in a
+ // zygote context), don't call into StrictMode, as it initializes
+ // the binder subsystem, which we don't want.
+ return null;
+ }
+
+ return StrictMode.allowThreadDiskReads();
+ }
+
+ private void setThreadPolicy(StrictMode.ThreadPolicy policy) {
+ if (mActivityThread != null && policy != null) {
+ StrictMode.setThreadPolicy(policy);
+ }
+ }
+
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
if (mPackageName.equals("android")) {
// Note: This branch is taken for system server and we don't need to setup
@@ -718,8 +736,11 @@
// Avoid the binder call when the package is the current application package.
// The activity manager will perform ensure that dexopt is performed before
- // spinning up the process.
- if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) {
+ // spinning up the process. Similarly, don't call into binder when we don't
+ // have an ActivityThread object.
+ if (mActivityThread != null
+ && !Objects.equals(mPackageName, ActivityThread.currentPackageName())
+ && mIncludeCode) {
try {
ActivityThread.getPackageManager().notifyPackageUse(mPackageName,
PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE);
@@ -790,12 +811,12 @@
// mIncludeCode == false).
if (!mIncludeCode) {
if (mDefaultClassLoader == null) {
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(
"" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
librarySearchPath, libraryPermittedPath, mBaseClassLoader,
null /* classLoaderName */);
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
mAppComponentFactory = AppComponentFactory.DEFAULT;
}
@@ -822,7 +843,7 @@
if (mDefaultClassLoader == null) {
// Temporarily disable logging of disk reads on the Looper thread
// as this is early and necessary.
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
@@ -834,18 +855,18 @@
mApplicationInfo.classLoaderName, sharedLibraries);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
// Setup the class loader paths for profiling.
needToSetupJitProfiles = true;
}
if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
// Temporarily disable logging of disk reads on the Looper thread as this is necessary
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
try {
ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, libPaths);
} finally {
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
}
}
@@ -879,11 +900,11 @@
extraLibPaths.add("/product/lib" + abiSuffix);
}
if (!extraLibPaths.isEmpty()) {
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
try {
ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths);
} finally {
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
}
}
@@ -929,6 +950,15 @@
if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
return;
}
+
+ // If we use profiles, setup the dex reporter to notify package manager
+ // of any relevant dex loads. The idle maintenance job will use the information
+ // reported to optimize the loaded dex files.
+ // Note that we only need one global reporter per app.
+ // Make sure we do this before invoking app code for the first time so that we
+ // can capture the complete application startup.
+ BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
+
// Only set up profile support if the loaded apk has the same uid as the
// current process.
// Currently, we do not support profiling across different apps.
@@ -1639,6 +1669,19 @@
}
}
+ @UnsupportedAppUsage
+ public IServiceConnection lookupServiceDispatcher(ServiceConnection c,
+ Context context) {
+ synchronized (mServices) {
+ LoadedApk.ServiceDispatcher sd = null;
+ ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
+ if (map != null) {
+ sd = map.get(c);
+ }
+ return sd != null ? sd.getIServiceConnection() : null;
+ }
+ }
+
public final IServiceConnection forgetServiceDispatcher(Context context,
ServiceConnection c) {
synchronized (mServices) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0281e6a..b9d5907 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4770,14 +4770,16 @@
TemplateBindResult result) {
boolean largeIconShown = bindLargeIcon(contentView, p);
boolean replyIconShown = bindReplyIcon(contentView, p);
+ boolean iconContainerVisible = largeIconShown || replyIconShown;
contentView.setViewVisibility(R.id.right_icon_container,
- largeIconShown || replyIconShown ? View.VISIBLE : View.GONE);
+ iconContainerVisible ? View.VISIBLE : View.GONE);
int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown);
contentView.setViewLayoutMarginEnd(R.id.line1, marginEnd);
contentView.setViewLayoutMarginEnd(R.id.text, marginEnd);
contentView.setViewLayoutMarginEnd(R.id.progress, marginEnd);
if (result != null) {
result.setIconMarginEnd(marginEnd);
+ result.setRightIconContainerVisible(iconContainerVisible);
}
}
@@ -6777,7 +6779,8 @@
mBuilder.setTextViewColorSecondary(contentView, R.id.big_text, p);
contentView.setViewVisibility(R.id.big_text,
TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
- contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.hasLargeIcon());
+ contentView.setBoolean(R.id.big_text, "setHasImage",
+ result.isRightIconContainerVisible());
return contentView;
}
@@ -8263,7 +8266,7 @@
customContent = customContent.clone();
remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
- remoteViews.setReapplyDisallowed();
+ remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
}
// also update the end margin if there is an image
Resources resources = mBuilder.mContext.getResources();
@@ -8394,7 +8397,7 @@
customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
remoteViews.removeAllViews(id);
remoteViews.addView(id, customContent);
- remoteViews.setReapplyDisallowed();
+ remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
}
return remoteViews;
}
@@ -9914,6 +9917,7 @@
*/
private static class TemplateBindResult {
int mIconMarginEnd;
+ boolean mRightIconContainerVisible;
/**
* Get the margin end that needs to be added to any fields that may overlap
@@ -9923,9 +9927,21 @@
return mIconMarginEnd;
}
+ /**
+ * Is the icon container visible on the right size because of the reply button or the
+ * right icon.
+ */
+ public boolean isRightIconContainerVisible() {
+ return mRightIconContainerVisible;
+ }
+
public void setIconMarginEnd(int iconMarginEnd) {
this.mIconMarginEnd = iconMarginEnd;
}
+
+ public void setRightIconContainerVisible(boolean iconContainerVisible) {
+ mRightIconContainerVisible = iconContainerVisible;
+ }
}
private static class StandardTemplateParams {
diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java
index 6747004..7cc3dab 100644
--- a/core/java/android/app/RecoverableSecurityException.java
+++ b/core/java/android/app/RecoverableSecurityException.java
@@ -16,15 +16,13 @@
package android.app;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
+import android.annotation.NonNull;
import android.content.Context;
-import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* Specialization of {@link SecurityException} that contains additional
@@ -35,18 +33,11 @@
* authentication credentials, or granting access.
* <p>
* If the receiving app is actively involved with the user, it should present
- * the contained recovery details to help the user make forward progress. The
- * {@link #showAsDialog(Activity)} and
- * {@link #showAsNotification(Context, String)} methods are provided as a
- * convenience, but receiving apps are encouraged to use
- * {@link #getUserMessage()} and {@link #getUserAction()} to integrate in a more
- * natural way if relevant.
+ * the contained recovery details to help the user make forward progress.
* <p class="note">
* Note: legacy code that receives this exception may treat it as a general
* {@link SecurityException}, and thus there is no guarantee that the messages
* contained will be shown to the end user.
- *
- * @hide
*/
public final class RecoverableSecurityException extends SecurityException implements Parcelable {
private static final String TAG = "RecoverableSecurityException";
@@ -78,53 +69,28 @@
* apps that observe {@link Activity#RESULT_OK} may choose to
* immediately retry their operation.
*/
- public RecoverableSecurityException(Throwable cause, CharSequence userMessage,
- RemoteAction userAction) {
+ public RecoverableSecurityException(@NonNull Throwable cause, @NonNull CharSequence userMessage,
+ @NonNull RemoteAction userAction) {
super(cause.getMessage());
- mUserMessage = Preconditions.checkNotNull(userMessage);
- mUserAction = Preconditions.checkNotNull(userAction);
- }
-
- /** {@hide} */
- @Deprecated
- public RecoverableSecurityException(Throwable cause, CharSequence userMessage,
- CharSequence userActionTitle, PendingIntent userAction) {
- this(cause, userMessage,
- new RemoteAction(
- Icon.createWithResource("android",
- com.android.internal.R.drawable.ic_restart),
- userActionTitle, userActionTitle, userAction));
+ mUserMessage = Objects.requireNonNull(userMessage);
+ mUserAction = Objects.requireNonNull(userAction);
}
/**
* Return short message describing the issue for end user audiences, which
* may be shown in a notification or dialog.
*/
- public CharSequence getUserMessage() {
+ public @NonNull CharSequence getUserMessage() {
return mUserMessage;
}
/**
* Return primary action that will initiate the recovery.
*/
- public RemoteAction getUserAction() {
+ public @NonNull RemoteAction getUserAction() {
return mUserAction;
}
- /** @removed */
- @Deprecated
- public void showAsNotification(Context context) {
- final NotificationManager nm = context.getSystemService(NotificationManager.class);
-
- // Create a channel per-sender, since we don't want one poorly behaved
- // remote app to cause all of our notifications to be blocked
- final String channelId = TAG + "_" + mUserAction.getActionIntent().getCreatorUid();
- nm.createNotificationChannel(new NotificationChannel(channelId, TAG,
- NotificationManager.IMPORTANCE_DEFAULT));
-
- showAsNotification(context, channelId);
- }
-
/**
* Convenience method that will show a very simple notification populated
* with the details from this exception.
@@ -142,6 +108,7 @@
* @param channelId the {@link NotificationChannel} to use, which must have
* been already created using
* {@link NotificationManager#createNotificationChannel}.
+ * @hide
*/
public void showAsNotification(Context context, String channelId) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -167,6 +134,8 @@
* <p>
* This method will only display the most recent exception from any single
* remote UID; dialogs from older exceptions will always be replaced.
+ *
+ * @hide
*/
public void showAsDialog(Activity activity) {
final LocalDialog dialog = new LocalDialog();
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 85fe99d..ebbf317 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -93,6 +93,22 @@
/** The user selected one of the choices from {@link #getChoices}. */
public static final int SOURCE_CHOICE = 1;
+ /** @hide */
+ @IntDef(prefix = {"EDIT_CHOICES_BEFORE_SENDING_"},
+ value = {EDIT_CHOICES_BEFORE_SENDING_AUTO, EDIT_CHOICES_BEFORE_SENDING_DISABLED,
+ EDIT_CHOICES_BEFORE_SENDING_ENABLED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EditChoicesBeforeSending {}
+
+ /** The platform will determine whether choices will be edited before being sent to the app. */
+ public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0;
+
+ /** Tapping on a choice should send the input immediately, without letting the user edit it. */
+ public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1;
+
+ /** Tapping on a choice should let the user edit the input before it is sent to the app. */
+ public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2;
+
// Flags bitwise-ored to mFlags
private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
@@ -103,17 +119,25 @@
private final CharSequence mLabel;
private final CharSequence[] mChoices;
private final int mFlags;
+ @EditChoicesBeforeSending private final int mEditChoicesBeforeSending;
private final Bundle mExtras;
private final ArraySet<String> mAllowedDataTypes;
private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
- int flags, Bundle extras, ArraySet<String> allowedDataTypes) {
+ int flags, int editChoicesBeforeSending, Bundle extras,
+ ArraySet<String> allowedDataTypes) {
this.mResultKey = resultKey;
this.mLabel = label;
this.mChoices = choices;
this.mFlags = flags;
+ this.mEditChoicesBeforeSending = editChoicesBeforeSending;
this.mExtras = extras;
this.mAllowedDataTypes = allowedDataTypes;
+ if (getEditChoicesBeforeSending() == EDIT_CHOICES_BEFORE_SENDING_ENABLED
+ && !getAllowFreeFormInput()) {
+ throw new IllegalArgumentException(
+ "setEditChoicesBeforeSending requires setAllowFreeFormInput");
+ }
}
/**
@@ -169,6 +193,15 @@
}
/**
+ * Gets whether tapping on a choice should let the user edit the input before it is sent to the
+ * app.
+ */
+ @EditChoicesBeforeSending
+ public int getEditChoicesBeforeSending() {
+ return mEditChoicesBeforeSending;
+ }
+
+ /**
* Get additional metadata carried around with this remote input.
*/
public Bundle getExtras() {
@@ -185,6 +218,8 @@
private CharSequence mLabel;
private CharSequence[] mChoices;
private int mFlags = DEFAULT_FLAGS;
+ @EditChoicesBeforeSending
+ private int mEditChoicesBeforeSending = EDIT_CHOICES_BEFORE_SENDING_AUTO;
/**
* Create a builder object for {@link RemoteInput} objects.
@@ -269,7 +304,20 @@
*/
@NonNull
public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
- setFlag(mFlags, allowFreeFormTextInput);
+ setFlag(FLAG_ALLOW_FREE_FORM_INPUT, allowFreeFormTextInput);
+ return this;
+ }
+
+ /**
+ * Specifies whether tapping on a choice should let the user edit the input before it is
+ * sent to the app. The default is {@link #EDIT_CHOICES_BEFORE_SENDING_AUTO}.
+ *
+ * It cannot be used if {@link #setAllowFreeFormInput} has been set to false.
+ */
+ @NonNull
+ public Builder setEditChoicesBeforeSending(
+ @EditChoicesBeforeSending int editChoicesBeforeSending) {
+ mEditChoicesBeforeSending = editChoicesBeforeSending;
return this;
}
@@ -312,8 +360,8 @@
*/
@NonNull
public RemoteInput build() {
- return new RemoteInput(
- mResultKey, mLabel, mChoices, mFlags, mExtras, mAllowedDataTypes);
+ return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mEditChoicesBeforeSending,
+ mExtras, mAllowedDataTypes);
}
}
@@ -322,6 +370,7 @@
mLabel = in.readCharSequence();
mChoices = in.readCharSequenceArray();
mFlags = in.readInt();
+ mEditChoicesBeforeSending = in.readInt();
mExtras = in.readBundle();
mAllowedDataTypes = (ArraySet<String>) in.readArraySet(null);
}
@@ -507,6 +556,7 @@
out.writeCharSequence(mLabel);
out.writeCharSequenceArray(mChoices);
out.writeInt(mFlags);
+ out.writeInt(mEditChoicesBeforeSending);
out.writeBundle(mExtras);
out.writeArraySet(mAllowedDataTypes);
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 16f6bda..5fa8526 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -685,6 +685,13 @@
* the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
* this API.</p>
*
+ * <p>To use this API, apps targeting API {@link android.os.Build.VERSION_CODES#Q} or later must
+ * specify the foreground service type using attribute
+ * {@link android.R.attr#foregroundServiceType} in service element of manifest file, otherwise
+ * a SecurityException is thrown when this API is called. Apps targeting API older than
+ * {@link android.os.Build.VERSION_CODES#Q} do not need to specify the foreground service type
+ * </p>
+ *
* @param id The identifier for this notification as per
* {@link NotificationManager#notify(int, Notification)
* NotificationManager.notify(int, Notification)}; must not be 0.
@@ -700,7 +707,7 @@
} catch (RemoteException ex) {
}
}
-
+
/**
* Synonym for {@link #stopForeground(int)}.
* @param removeNotification If true, the {@link #STOP_FOREGROUND_REMOVE} flag
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 32fc0dc..9dcd122 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -119,6 +119,7 @@
/**
* @deprecated Use {@link #addConfig(long, byte[])}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean addConfiguration(long configKey, byte[] config) {
try {
@@ -154,6 +155,7 @@
/**
* @deprecated Use {@link #removeConfig(long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean removeConfiguration(long configKey) {
try {
@@ -222,6 +224,7 @@
/**
* @deprecated Use {@link #setBroadcastSubscriber(PendingIntent, long, long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean setBroadcastSubscriber(
long configKey, long subscriberId, PendingIntent pendingIntent) {
@@ -275,6 +278,7 @@
/**
* @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
try {
@@ -312,6 +316,7 @@
/**
* @deprecated Use {@link #getReports(long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public @Nullable byte[] getData(long configKey) {
try {
@@ -348,6 +353,7 @@
/**
* @deprecated Use {@link #getStatsMetadata()}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public @Nullable byte[] getMetadata() {
try {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index dfe371c..43e1836 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -59,6 +59,7 @@
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.IBiometricService;
import android.hardware.camera2.CameraManager;
+import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
@@ -164,9 +165,9 @@
import android.view.accessibility.CaptioningManager;
import android.view.autofill.AutofillManager;
import android.view.autofill.IAutoFillManager;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.IContentCaptureManager;
import android.view.inputmethod.InputMethodManager;
-import android.view.intelligence.ContentCaptureManager;
-import android.view.intelligence.IIntelligenceManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -386,6 +387,14 @@
return new DisplayManager(ctx.getOuterContext());
}});
+ registerService(Context.COLOR_DISPLAY_SERVICE, ColorDisplayManager.class,
+ new CachedServiceFetcher<ColorDisplayManager>() {
+ @Override
+ public ColorDisplayManager createService(ContextImpl ctx) {
+ return new ColorDisplayManager();
+ }
+ });
+
// InputMethodManager has its own cache strategy based on display id to support apps that
// still assume InputMethodManager is a per-process singleton and it's safe to directly
// access internal fields via reflection. Hence directly use ServiceFetcher instead of
@@ -1061,7 +1070,7 @@
if (outerContext.isContentCaptureSupported()) {
IBinder b = ServiceManager
.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
- IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
+ IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
return new ContentCaptureManager(outerContext, service);
}
return null;
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index dc2f983..31521a3 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -331,7 +331,7 @@
writeTo.flush();
}
} catch (IOException ioe) {
- throw new RuntimeException("Error while reading/writing ", ioe);
+ Log.w(TAG, "Error while reading/writing to streams");
} finally {
IoUtils.closeQuietly(readFrom);
IoUtils.closeQuietly(writeTo);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 78b7d48..2990b57 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -75,6 +75,9 @@
/** The current windowing mode of the configuration. */
private @WindowingMode int mWindowingMode;
+ /** The display windowing mode of the configuration */
+ private @WindowingMode int mDisplayWindowingMode;
+
/** Windowing mode is currently not defined. */
public static final int WINDOWING_MODE_UNDEFINED = 0;
/** Occupies the full area of the screen or the parent container. */
@@ -176,6 +179,10 @@
/** Bit that indicates that the {@link #mRotation} changed.
* @hide */
public static final int WINDOW_CONFIG_ROTATION = 1 << 5;
+ /** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
+ * @hide */
+ public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 6;
+
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
WINDOW_CONFIG_BOUNDS,
@@ -184,6 +191,7 @@
WINDOW_CONFIG_ACTIVITY_TYPE,
WINDOW_CONFIG_ALWAYS_ON_TOP,
WINDOW_CONFIG_ROTATION,
+ WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
})
public @interface WindowConfig {}
@@ -211,6 +219,7 @@
dest.writeInt(mActivityType);
dest.writeInt(mAlwaysOnTop);
dest.writeInt(mRotation);
+ dest.writeInt(mDisplayWindowingMode);
}
private void readFromParcel(Parcel source) {
@@ -220,6 +229,7 @@
mActivityType = source.readInt();
mAlwaysOnTop = source.readInt();
mRotation = source.readInt();
+ mDisplayWindowingMode = source.readInt();
}
@Override
@@ -322,6 +332,12 @@
return mWindowingMode;
}
+ /** @hide */
+ public void setDisplayWindowingMode(@WindowingMode int windowingMode) {
+ mDisplayWindowingMode = windowingMode;
+ }
+
+
public void setActivityType(@ActivityType int activityType) {
if (mActivityType == activityType) {
return;
@@ -351,6 +367,7 @@
setActivityType(other.mActivityType);
setAlwaysOnTop(other.mAlwaysOnTop);
setRotation(other.mRotation);
+ setDisplayWindowingMode(other.mDisplayWindowingMode);
}
/** Set this object to completely undefined.
@@ -367,6 +384,7 @@
setActivityType(ACTIVITY_TYPE_UNDEFINED);
setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
setRotation(ROTATION_UNDEFINED);
+ setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED);
}
/**
@@ -407,6 +425,11 @@
changed |= WINDOW_CONFIG_ROTATION;
setRotation(delta.mRotation);
}
+ if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED
+ && mDisplayWindowingMode != delta.mDisplayWindowingMode) {
+ changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
+ setDisplayWindowingMode(delta.mDisplayWindowingMode);
+ }
return changed;
}
@@ -455,6 +478,11 @@
changes |= WINDOW_CONFIG_ROTATION;
}
+ if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED)
+ && mDisplayWindowingMode != other.mDisplayWindowingMode) {
+ changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
+ }
+
return changes;
}
@@ -493,6 +521,8 @@
if (n != 0) return n;
n = mRotation - that.mRotation;
if (n != 0) return n;
+ n = mDisplayWindowingMode - that.mDisplayWindowingMode;
+ if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -522,6 +552,7 @@
result = 31 * result + mActivityType;
result = 31 * result + mAlwaysOnTop;
result = 31 * result + mRotation;
+ result = 31 * result + mDisplayWindowingMode;
return result;
}
@@ -531,6 +562,7 @@
return "{ mBounds=" + mBounds
+ " mAppBounds=" + mAppBounds
+ " mWindowingMode=" + windowingModeToString(mWindowingMode)
+ + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
+ " mActivityType=" + activityTypeToString(mActivityType)
+ " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
+ " mRotation=" + (mRotation == ROTATION_UNDEFINED
@@ -603,7 +635,8 @@
* @hide
*/
public boolean hasWindowDecorCaption() {
- return mWindowingMode == WINDOWING_MODE_FREEFORM;
+ return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM
+ || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM);
}
/**
@@ -640,6 +673,15 @@
}
/**
+ * Returns true if the windowingMode represents a split window.
+ * @hide
+ */
+ public static boolean isSplitScreenWindowingMode(int windowingMode) {
+ return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ /**
* Returns true if the windows associated with this window configuration can receive input keys.
* @hide
*/
diff --git a/core/java/android/app/admin/DelegatedAdminReceiver.java b/core/java/android/app/admin/DelegatedAdminReceiver.java
new file mode 100644
index 0000000..9605382
--- /dev/null
+++ b/core/java/android/app/admin/DelegatedAdminReceiver.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static android.app.admin.DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS;
+import static android.app.admin.DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_NETWORK_LOGS_COUNT;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.security.KeyChain;
+import android.util.Log;
+
+/**
+ * Base class for delegated apps to handle callbacks related to their delegated capabilities.
+ *
+ * <p>Delegated apps are apps that receive additional capabilities from the profile owner or
+ * device owner apps. Some of these capabilities involve the framework calling into the apps.
+ * To receive these callbacks, delegated apps should subclass this class and override the
+ * appropriate methods here. The subclassed receiver needs to be published in the app's
+ * manifest, with appropriate intent filters to mark which callbacks the receiver is interested
+ * in. An app can have multiple receivers as long as they listen for disjoint set of callbacks.
+ * For the manifest definitions, it must be protected by the
+ * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission to ensure only
+ * the system can trigger these callbacks.
+ *
+ * <p>The callback methods happen on the main thread of the process. Thus long running
+ * operations must be done on another thread. Note that because a receiver
+ * is done once returning from its onReceive function, such long-running operations
+ * should probably be done in a {@link Service}.
+ *
+ * @see DevicePolicyManager#setDelegatedScopes
+ * @see DeviceAdminReceiver
+ */
+public class DelegatedAdminReceiver extends BroadcastReceiver {
+ private static final String TAG = "DelegatedAdminReceiver";
+
+ /**
+ * Allows this receiver to select the alias for a private key and certificate pair for
+ * authentication. If this method returns null, the default {@link android.app.Activity} will
+ * be shown that lets the user pick a private key and certificate pair.
+ *
+ * <p> This callback is only applicable if the delegated app has
+ * {@link DevicePolicyManager#DELEGATION_CERT_SELECTION} capability. Additionally, it must
+ * declare an intent fitler for {@link DeviceAdminReceiver#ACTION_CHOOSE_PRIVATE_KEY_ALIAS}
+ * in the receiver's manifest in order to receive this callback.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param uid The uid asking for the private key and certificate pair.
+ * @param uri The URI to authenticate, may be null.
+ * @param alias The alias preselected by the client, or null.
+ * @return The private key alias to return and grant access to.
+ * @see KeyChain#choosePrivateKeyAlias
+ */
+ public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
+ String alias) {
+ return null;
+ }
+
+ /**
+ * Called each time a new batch of network logs can be retrieved. This callback method will only
+ * ever be called when network logging is enabled. The logs can only be retrieved while network
+ * logging is enabled.
+ *
+ * <p>If a secondary user or profile is created, this callback won't be received until all users
+ * become affiliated again (even if network logging is enabled). It will also no longer be
+ * possible to retrieve the network logs batch with the most recent {@code batchToken} provided
+ * by this callback. See {@link DevicePolicyManager#setAffiliationIds}.
+ *
+ * <p> This callback is only applicable if the delegated app has
+ * {@link DevicePolicyManager#DELEGATION_NETWORK_LOGGING} capability. Additionally, it must
+ * declare an intent fitler for {@link DeviceAdminReceiver#ACTION_NETWORK_LOGS_AVAILABLE} in the
+ * receiver's manifest in order to receive this callback.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param batchToken The token representing the current batch of network logs.
+ * @param networkLogsCount The total count of events in the current batch of network logs.
+ * @see DevicePolicyManager#retrieveNetworkLogs
+ */
+ public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
+ int networkLogsCount) {
+ }
+
+ /**
+ * Intercept delegated device administrator broadcasts. Implementations should not override
+ * this method; implement the convenience callbacks for each action instead.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (ACTION_CHOOSE_PRIVATE_KEY_ALIAS.equals(action)) {
+ int uid = intent.getIntExtra(EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, -1);
+ Uri uri = intent.getParcelableExtra(EXTRA_CHOOSE_PRIVATE_KEY_URI);
+ String alias = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_ALIAS);
+ String chosenAlias = onChoosePrivateKeyAlias(context, intent, uid, uri, alias);
+ setResultData(chosenAlias);
+ } else if (ACTION_NETWORK_LOGS_AVAILABLE.equals(action)) {
+ long batchToken = intent.getLongExtra(EXTRA_NETWORK_LOGS_TOKEN, -1);
+ int networkLogsCount = intent.getIntExtra(EXTRA_NETWORK_LOGS_COUNT, 0);
+ onNetworkLogsAvailable(context, intent, batchToken, networkLogsCount);
+ } else {
+ Log.w(TAG, "Unhandled broadcast: " + action);
+ }
+ }
+}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 1c9477d..5a7124e 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -297,7 +296,7 @@
/**
* Broadcast action: notify that a new batch of network logs is ready to be collected.
* @see DeviceAdminReceiver#onNetworkLogsAvailable
- * @hide
+ * @see DelegatedAdminReceiver#onNetworkLogsAvailable
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
@@ -426,7 +425,11 @@
*/
public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1;
- /** @hide */
+ /**
+ * Broadcast action: notify that some app is attempting to choose a KeyChain key.
+ * @see DeviceAdminReceiver#onChoosePrivateKeyAlias
+ * @see DelegatedAdminReceiver#onChoosePrivateKeyAlias
+ */
public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS =
"android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
@@ -755,7 +758,6 @@
* @deprecated Do not use
*/
@Deprecated
- @SystemApi
public void onReadyForUserInitialization(Context context, Intent intent) {
}
diff --git a/core/java/android/app/admin/DevicePolicyEventLogger.java b/core/java/android/app/admin/DevicePolicyEventLogger.java
new file mode 100644
index 0000000..c89d868
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyEventLogger.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.stats.devicepolicy.nano.StringList;
+import android.util.StatsLog;
+
+import com.android.framework.protobuf.nano.MessageNano;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+/**
+ * A wrapper for logging managed device events using {@link StatsLog}.
+ * <p/>
+ * This class allows chaining - each of its methods returns a reference to the current instance.
+ * <p/>
+ * Example usage:
+ * <code><pre>
+ * import android.stats.devicepolicy.DevicePolicyEnums;
+ *
+ * DevicePolicyEventLogger
+ * .createEvent(DevicePolicyEnums.USER_RESTRICTION_CHANGED)
+ * .setAdmin(who)
+ * .setString(key)
+ * .setBoolean(enabledFromThisOwner)
+ * .write();
+ * </pre></code>
+ *
+ * @see StatsLog
+ * @hide
+ */
+public final class DevicePolicyEventLogger {
+ private final int mEventId;
+ private int mIntValue;
+ private boolean mBooleanValue;
+ private long mTimePeriodMs;
+ private String[] mStringArrayValue;
+ private String mAdminPackageName;
+
+ private DevicePolicyEventLogger(int eventId) {
+ mEventId = eventId;
+ }
+
+ /**
+ * Creates a new {@link DevicePolicyEventLogger} instance for the specified
+ * <code>eventId</code>.
+ *
+ * @param eventId one of {@link android.stats.devicepolicy.DevicePolicyEnums} as defined in
+ * <code>core/proto/android/stats/devicepolicy/device_policy_enums.proto</code>
+ */
+ public static DevicePolicyEventLogger createEvent(int eventId) {
+ return new DevicePolicyEventLogger(eventId);
+ }
+
+ /**
+ * Returns the event id.
+ */
+ @VisibleForTesting
+ public int getEventId() {
+ return mEventId;
+ }
+
+ /**
+ * Sets a generic <code>int</code> value.
+ */
+ public DevicePolicyEventLogger setInt(int value) {
+ mIntValue = value;
+ return this;
+ }
+
+ /**
+ * Returns the generic <code>int</code> value.
+ */
+ @VisibleForTesting
+ public int getInt() {
+ return mIntValue;
+ }
+
+ /**
+ * Sets a generic <code>boolean</code> value.
+ */
+ public DevicePolicyEventLogger setBoolean(boolean value) {
+ mBooleanValue = value;
+ return this;
+ }
+
+ /**
+ * Returns the generic <code>boolean</code> value.
+ */
+ @VisibleForTesting
+ public boolean getBoolean() {
+ return mBooleanValue;
+ }
+
+ /**
+ * Sets a time period in milliseconds.
+ */
+ public DevicePolicyEventLogger setTimePeriod(long timePeriodMillis) {
+ mTimePeriodMs = timePeriodMillis;
+ return this;
+ }
+
+ /**
+ * Returns the time period in milliseconds.
+ */
+ @VisibleForTesting
+ public long getTimePeriod() {
+ return mTimePeriodMs;
+ }
+
+ /**
+ * Sets generic <code>String</code> values.
+ */
+ public DevicePolicyEventLogger setStrings(String... values) {
+ mStringArrayValue = values;
+ return this;
+ }
+
+ /**
+ * Sets generic <code>String</code> values.
+ * <p/>
+ * {@link #write()} logs the concatenation of <code>value</code> and <code>values</code>,
+ * in that order.
+ */
+ public DevicePolicyEventLogger setStrings(String value, String[] values) {
+ Preconditions.checkNotNull(values, "values parameter cannot be null");
+ mStringArrayValue = new String[values.length + 1];
+ mStringArrayValue[0] = value;
+ System.arraycopy(values, 0, mStringArrayValue, 1, values.length);
+ return this;
+ }
+
+ /**
+ * Sets generic <code>String</code> values.
+ * <p/>
+ * {@link #write()} logs the concatenation of <code>value1</code>, <code>value2</code>
+ * and <code>values</code>, in that order.
+ */
+ public DevicePolicyEventLogger setStrings(String value1, String value2, String[] values) {
+ Preconditions.checkNotNull(values, "values parameter cannot be null");
+ mStringArrayValue = new String[values.length + 2];
+ mStringArrayValue[0] = value1;
+ mStringArrayValue[1] = value2;
+ System.arraycopy(values, 0, mStringArrayValue, 2, values.length);
+ return this;
+ }
+
+ /**
+ * Returns the generic <code>String[]</code> value.
+ */
+ @VisibleForTesting
+ public String[] getStringArray() {
+ return mStringArrayValue;
+ }
+
+ /**
+ * Sets the package name of the admin application.
+ */
+ public DevicePolicyEventLogger setAdmin(@Nullable String packageName) {
+ mAdminPackageName = packageName;
+ return this;
+ }
+
+ /**
+ * Retrieves the package name of the admin application from the {@link ComponentName}.
+ */
+ public DevicePolicyEventLogger setAdmin(@Nullable ComponentName componentName) {
+ mAdminPackageName = (componentName != null ? componentName.getPackageName() : null);
+ return this;
+ }
+
+ /**
+ * Returns the package name of the admin application.
+ */
+ @VisibleForTesting
+ public String getAdminPackageName() {
+ return mAdminPackageName;
+ }
+
+ /**
+ * Writes the metric to {@link StatsLog}.
+ */
+ public void write() {
+ byte[] bytes = stringArrayValueToBytes(mStringArrayValue);
+ StatsLog.write(StatsLog.DEVICE_POLICY_EVENT, mEventId, mAdminPackageName, mIntValue,
+ mBooleanValue, mTimePeriodMs, bytes);
+ }
+
+ /**
+ * Converts the <code>String[] array</code> to <code>byte[]</code>.
+ * <p/>
+ * We can't log <code>String[]</code> using {@link StatsLog}. The convention is to assign
+ * the array to a proto object and convert it to <code>byte[]</code>.
+ */
+ private static byte[] stringArrayValueToBytes(String[] array) {
+ if (array == null) {
+ return null;
+ }
+ StringList stringList = new StringList();
+ stringList.stringValue = array;
+ return MessageNano.toByteArray(stringList);
+ }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 98d2a40..03e5933 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.IntDef;
@@ -49,6 +50,8 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
+import android.net.NetworkUtils;
+import android.net.PrivateDnsConnectivityChecker;
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Binder;
@@ -64,6 +67,7 @@
import android.os.UserManager;
import android.os.UserManager.UserOperationException;
import android.os.UserManager.UserOperationResult;
+import android.provider.CalendarContract;
import android.provider.ContactsContract.Directory;
import android.provider.Settings;
import android.security.AttestedKeyPair;
@@ -79,6 +83,7 @@
import android.service.restrictions.RestrictionsReceiver;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -1121,6 +1126,64 @@
"android.app.extra.PROVISIONING_USE_MOBILE_DATA";
/**
+ * A String extra holding the provisioning trigger. It could be one of
+ * {@link #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT}, {@link #PROVISIONING_TRIGGER_QR_CODE},
+ * {@link #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER} or {@link
+ * #PROVISIONING_TRIGGER_UNSPECIFIED}.
+ *
+ * <p>Use in an intent with action {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_TRIGGER =
+ "android.app.extra.PROVISIONING_TRIGGER";
+
+ /**
+ * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+ * trigger has not been specified.
+ * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+ * @see #PROVISIONING_TRIGGER_QR_CODE
+ * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+ * @hide
+ */
+ @SystemApi
+ public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0;
+
+ /**
+ * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+ * trigger is cloud enrollment.
+ * @see #PROVISIONING_TRIGGER_QR_CODE
+ * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+ * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @hide
+ */
+ @SystemApi
+ public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1;
+
+ /**
+ * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+ * trigger is the QR code scanner.
+ * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+ * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+ * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @hide
+ */
+ @SystemApi
+ public static final int PROVISIONING_TRIGGER_QR_CODE = 2;
+
+ /**
+ * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+ * trigger is persistent device owner enrollment.
+ * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+ * @see #PROVISIONING_TRIGGER_QR_CODE
+ * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @hide
+ */
+ @SystemApi
+ public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3;
+
+ /**
* This MIME type is used for starting the device owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -1320,6 +1383,73 @@
= "android.app.action.SET_NEW_PASSWORD";
/**
+ * Constant for {@link #getPasswordComplexity()}: no password.
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ */
+ public static final int PASSWORD_COMPLEXITY_NONE = 0;
+
+ /**
+ * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following:
+ * <ul>
+ * <li>pattern
+ * <li>PIN with repeating (4444) or ordered (1234, 4321, 2468) sequences
+ * </ul>
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ *
+ * @see #PASSWORD_QUALITY_SOMETHING
+ * @see #PASSWORD_QUALITY_NUMERIC
+ */
+ public static final int PASSWORD_COMPLEXITY_LOW = 0x10000;
+
+ /**
+ * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following:
+ * <ul>
+ * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at
+ * least 4
+ * <li>alphabetic, length at least 4
+ * <li>alphanumeric, length at least 4
+ * </ul>
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ *
+ * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX
+ * @see #PASSWORD_QUALITY_ALPHABETIC
+ * @see #PASSWORD_QUALITY_ALPHANUMERIC
+ */
+ public static final int PASSWORD_COMPLEXITY_MEDIUM = 0x30000;
+
+ /**
+ * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following:
+ * <ul>
+ * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at
+ * least 4
+ * <li>alphabetic, length at least 6
+ * <li>alphanumeric, length at least 6
+ * </ul>
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ *
+ * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX
+ * @see #PASSWORD_QUALITY_ALPHABETIC
+ * @see #PASSWORD_QUALITY_ALPHANUMERIC
+ */
+ public static final int PASSWORD_COMPLEXITY_HIGH = 0x50000;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PASSWORD_COMPLEXITY_"}, value = {
+ PASSWORD_COMPLEXITY_NONE,
+ PASSWORD_COMPLEXITY_LOW,
+ PASSWORD_COMPLEXITY_MEDIUM,
+ PASSWORD_COMPLEXITY_HIGH,
+ })
+ public @interface PasswordComplexity {}
+
+ /**
* Activity action: have the user enter a new password for the parent profile.
* If the intent is launched from within a managed profile, this will trigger
* entering a new password for the parent of the profile. In all other cases
@@ -1485,12 +1615,46 @@
/**
* Delegation of management of uninstalled packages. This scope grants access to the
- * {@code #setKeepUninstalledPackages} and {@code #getKeepUninstalledPackages} APIs.
+ * {@link #setKeepUninstalledPackages} and {@link #getKeepUninstalledPackages} APIs.
*/
public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES =
"delegation-keep-uninstalled-packages";
/**
+ * Grants access to {@link #setNetworkLoggingEnabled}, {@link #isNetworkLoggingEnabled} and
+ * {@link #retrieveNetworkLogs}. Once granted the delegated app will start receiving
+ * DelegatedAdminReceiver.onNetworkLogsAvailable() callback, and Device owner will no longer
+ * receive the DeviceAdminReceiver.onNetworkLogsAvailable() callback.
+ * There can be at most one app that has this delegation.
+ * If another app already had delegated network logging access,
+ * it will lose the delegation when a new app is delegated.
+ *
+ * <p> Can only be granted by Device Owner.
+ */
+ public static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
+
+ /**
+ * Grants access to selection of KeyChain certificates on behalf of requesting apps.
+ * Once granted the app will start receiving
+ * DelegatedAdminReceiver.onChoosePrivateKeyAlias. The caller (PO/DO) will
+ * no longer receive {@link DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+ * There can be at most one app that has this delegation.
+ * If another app already had delegated certificate selection access,
+ * it will lose the delegation when a new app is delegated.
+ *
+ * <p> Can be granted by Device Owner or Profile Owner.
+ */
+ public static final String DELEGATION_CERT_SELECTION = "delegation-cert-selection";
+
+
+ /**
+ * Delegation of silent APK installation via {@link android.content.pm.PackageInstaller} APIs.
+ *
+ * <p> Can only be delegated by Device Owner.
+ */
+ public static final String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation";
+
+ /**
* No management for current user in-effect. This is the default.
* @hide
*/
@@ -1974,6 +2138,35 @@
public @interface InstallUpdateCallbackErrorConstants {}
/**
+ * The selected mode has been set successfully. If the mode is
+ * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} then it implies the supplied host is valid
+ * and reachable.
+ */
+ public static final int PRIVATE_DNS_SET_SUCCESS = 0;
+
+ /**
+ * If the {@code privateDnsHost} provided was of a valid hostname but that host was found
+ * to not support DNS-over-TLS.
+ */
+ public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1;
+
+ /**
+ * General failure to set the Private DNS mode, not due to one of the reasons listed above.
+ */
+ public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"PRIVATE_DNS_SET_"}, value = {
+ PRIVATE_DNS_SET_SUCCESS,
+ PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING,
+ PRIVATE_DNS_SET_ERROR_FAILURE_SETTING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SetPrivateDnsModeResultConstants {}
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -2981,6 +3174,33 @@
}
/**
+ * Returns how complex the current user's screen lock is.
+ *
+ * <p>Note that when called from a profile which uses an unified challenge with its parent, the
+ * screen lock complexity of the parent will be returned. However, this API does not support
+ * explicitly querying the parent profile screen lock complexity via {@link
+ * #getParentProfileInstance}.
+ *
+ * @throws IllegalStateException if the user is not unlocked.
+ * @throws SecurityException if the calling application does not have the permission
+ * {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}
+ */
+ @PasswordComplexity
+ @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY)
+ public int getPasswordComplexity() {
+ throwIfParentInstance("getPasswordComplexity");
+ if (mService == null) {
+ return PASSWORD_COMPLEXITY_NONE;
+ }
+
+ try {
+ return mService.getPasswordComplexity();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* When called by a profile owner of a managed profile returns true if the profile uses unified
* challenge with its parent user.
*
@@ -9148,7 +9368,8 @@
}
/**
- * Called by a device owner to control the network logging feature.
+ * Called by a device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to
+ * control the network logging feature.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
* functions are recorded while network logging is active:
@@ -9185,16 +9406,17 @@
* all users to become affiliated. Therefore it's recommended that affiliation ids are set for
* new users as soon as possible after provisioning via {@link #setAffiliationIds}.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if called by a delegated app.
* @param enabled whether network logging should be enabled or not.
* @throws SecurityException if {@code admin} is not a device owner.
* @see #setAffiliationIds
* @see #retrieveNetworkLogs
*/
- public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
+ public void setNetworkLoggingEnabled(@Nullable ComponentName admin, boolean enabled) {
throwIfParentInstance("setNetworkLoggingEnabled");
try {
- mService.setNetworkLoggingEnabled(admin, enabled);
+ mService.setNetworkLoggingEnabled(admin, mContext.getPackageName(), enabled);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -9204,7 +9426,8 @@
* Return whether network logging is enabled by a device owner.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with. Can only
- * be {@code null} if the caller has MANAGE_USERS permission.
+ * be {@code null} if the caller is a delegated app with {@link #DELEGATION_NETWORK_LOGGING}
+ * or has MANAGE_USERS permission.
* @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a device owner and caller has
* no MANAGE_USERS permission
@@ -9212,14 +9435,15 @@
public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin) {
throwIfParentInstance("isNetworkLoggingEnabled");
try {
- return mService.isNetworkLoggingEnabled(admin);
+ return mService.isNetworkLoggingEnabled(admin, mContext.getPackageName());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
- * Called by device owner to retrieve the most recent batch of network logging events.
+ * Called by device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to retrieve
+ * the most recent batch of network logging events.
* A device owner has to provide a batchToken provided as part of
* {@link DeviceAdminReceiver#onNetworkLogsAvailable} callback. If the token doesn't match the
* token of the most recent available batch of logs, {@code null} will be returned.
@@ -9238,7 +9462,8 @@
* by {@link DeviceAdminReceiver#onNetworkLogsAvailable}. See
* {@link DevicePolicyManager#setAffiliationIds}.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if called by a delegated app.
* @param batchToken A token of the batch to retrieve
* @return A new batch of network logs which is a list of {@link NetworkEvent}. Returns
* {@code null} if the batch represented by batchToken is no longer available or if
@@ -9248,11 +9473,11 @@
* @see #setAffiliationIds
* @see DeviceAdminReceiver#onNetworkLogsAvailable
*/
- public @Nullable List<NetworkEvent> retrieveNetworkLogs(@NonNull ComponentName admin,
+ public @Nullable List<NetworkEvent> retrieveNetworkLogs(@Nullable ComponentName admin,
long batchToken) {
throwIfParentInstance("retrieveNetworkLogs");
try {
- return mService.retrieveNetworkLogs(admin, batchToken);
+ return mService.retrieveNetworkLogs(admin, mContext.getPackageName(), batchToken);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -9839,6 +10064,16 @@
* Sets the global Private DNS mode and host to be used.
* May only be called by the device owner.
*
+ * <p>Note that in case a Private DNS resolver is specified, the method is blocking as it
+ * will perform a connectivity check to the resolver, to ensure it is valid. Because of that,
+ * the method should not be called on any thread that relates to user interaction, such as the
+ * UI thread.
+ *
+ * <p>In case a VPN is used in conjunction with Private DNS resolver, the Private DNS resolver
+ * must be reachable both from within and outside the VPN. Otherwise, the device may lose
+ * the ability to resolve hostnames as system traffic to the resolver may not go through the
+ * VPN.
+ *
* @param admin which {@link DeviceAdminReceiver} this request is associated with.
* @param mode Which mode to set - either {@code PRIVATE_DNS_MODE_OPPORTUNISTIC} or
* {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}.
@@ -9848,6 +10083,9 @@
* @param privateDnsHost The hostname of a server that implements DNS over TLS (RFC7858), if
* {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} was specified as the mode,
* null otherwise.
+ *
+ * @return One of the values in {@link SetPrivateDnsModeResultConstants}.
+ *
* @throws IllegalArgumentException in the following cases: if a {@code privateDnsHost} was
* provided but the mode was not {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}, if the mode
* specified was {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} but {@code privateDnsHost} does
@@ -9855,15 +10093,23 @@
*
* @throws SecurityException if the caller is not the device owner.
*/
- public void setGlobalPrivateDns(@NonNull ComponentName admin,
+ public int setGlobalPrivateDns(@NonNull ComponentName admin,
@PrivateDnsMode int mode, @Nullable String privateDnsHost) {
throwIfParentInstance("setGlobalPrivateDns");
+
if (mService == null) {
- return;
+ return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
+ }
+
+ if (mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME && !TextUtils.isEmpty(privateDnsHost)
+ && NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+ if (!PrivateDnsConnectivityChecker.canConnectToPrivateDnsServer(privateDnsHost)) {
+ return PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING;
+ }
}
try {
- mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
+ return mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -10092,7 +10338,134 @@
if (mService != null) {
try {
return mService.isPackageAllowedToAccessCalendarForUser(packageName,
- mContext.getUserId());
+ myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+ *
+ * <p>To query for a specific user, use
+ * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
+ * that user, and get a {@link DevicePolicyManager} from this context.
+ *
+ * @return the set of names of packages that were previously whitelisted via
+ * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+ * empty set if none have been whitelisted.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ * @hide
+ */
+ public @NonNull Set<String> getCrossProfileCalendarPackages() {
+ throwIfParentInstance("getCrossProfileCalendarPackages");
+ if (mService != null) {
+ try {
+ return new ArraySet<>(mService.getCrossProfileCalendarPackagesForUser(
+ myUserId()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ /**
+ * 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:
+ * <ul>
+ * <li>The device is in Lock Task (therefore there is also a Device Owner app on the
+ * device)</li>
+ * <li>The Lock Task feature {@link DevicePolicyManager#LOCK_TASK_FEATURE_SYSTEM_INFO} is
+ * not enabled, so the system info in the status bar is not visible</li>
+ * <li>The device does not have a secure lock screen (e.g. it has no lock screen or has
+ * swipe-to-unlock)</li>
+ * <li>The device is not in the middle of an ephemeral user session</li>
+ * </ul>
+ *
+ * <p>Publicly-accessible dedicated devices don't have the same privacy model as
+ * personally-used devices. In particular, user consent popups don't make sense as a barrier to
+ * accessing persistent data on these devices since the user giving consent and the user whose
+ * data is on the device are unlikely to be the same. These consent popups prevent the true
+ * remote management of these devices.
+ *
+ * <p>This condition is not sufficient to cover APIs that would access data that only lives for
+ * the duration of the user's session, since the user has an expectation of privacy in these
+ * conditions that more closely resembles use of a personal device. In those cases, see {@link
+ * #isUnattendedManagedKiosk()}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isManagedKiosk() {
+ throwIfParentInstance("isManagedKiosk");
+ if (mService != null) {
+ try {
+ return mService.isManagedKiosk();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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:
+ * <ul>
+ * <li>The device is being used as a managed kiosk, as defined in the CDD and verified at
+ * {@link #isManagedKiosk()}</li>
+ * <li>The device has not received user input for at least 30 minutes</li>
+ * </ul>
+ *
+ * <p>See {@link #isManagedKiosk()} for context. This is a stronger requirement that also
+ * ensures that the device hasn't been interacted with recently, making it an appropriate check
+ * for privacy-sensitive APIs that wouldn't be appropriate during an active user session.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isUnattendedManagedKiosk() {
+ throwIfParentInstance("isUnattendedManagedKiosk");
+ if (mService != null) {
+ try {
+ return mService.isUnattendedManagedKiosk();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Starts an activity to view calendar events in the managed profile.
+ *
+ * @param eventId the id of the event to be viewed.
+ * @param start the start time of the event.
+ * @param end the end time of the event.
+ * @param allDay if the event is an all-day event.
+ * @param flags flags to be set for the intent
+ * @return {@code true} if the activity is started successfully. {@code false} otherwise.
+ *
+ * @see CalendarContract#startViewCalendarEventInManagedProfile(Context, String, long, long,
+ * long, boolean, int)
+ *
+ * @hide
+ */
+ public boolean startViewCalendarEventInManagedProfile(long eventId, long start, long end,
+ boolean allDay, int flags) {
+ throwIfParentInstance("startViewCalendarEventInManagedProfile");
+ if (mService != null) {
+ try {
+ return mService.startViewCalendarEventInManagedProfile(mContext.getPackageName(),
+ eventId, start, end, allDay, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index de92978..8765760 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -118,6 +118,12 @@
public abstract boolean isUserAffiliatedWithDevice(int userId);
/**
+ * Returns whether the calling package can install or uninstall packages without user
+ * interaction.
+ */
+ public abstract boolean canSilentlyInstallPackage(String callerPackage, int callerUid);
+
+ /**
* Reports that a profile has changed to use a unified or separate credential.
*
* @param userId User ID of the profile.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 297676d..568becf 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -82,6 +82,7 @@
boolean isActivePasswordSufficient(int userHandle, boolean parent);
boolean isProfileActivePasswordSufficientForParent(int userHandle);
+ int getPasswordComplexity();
boolean isUsingUnifiedPassword(in ComponentName admin);
int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent);
@@ -366,9 +367,9 @@
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
- void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
- boolean isNetworkLoggingEnabled(in ComponentName admin);
- List<NetworkEvent> retrieveNetworkLogs(in ComponentName admin, long batchToken);
+ void setNetworkLoggingEnabled(in ComponentName admin, in String packageName, boolean enabled);
+ boolean isNetworkLoggingEnabled(in ComponentName admin, in String packageName);
+ List<NetworkEvent> retrieveNetworkLogs(in ComponentName admin, in String packageName, long batchToken);
boolean bindDeviceAdminServiceAsUser(in ComponentName admin,
IApplicationThread caller, IBinder token, in Intent service,
@@ -415,7 +416,7 @@
boolean isMeteredDataDisabledPackageForUser(in ComponentName admin, String packageName, int userId);
- void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
+ int setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
int getGlobalPrivateDnsMode(in ComponentName admin);
String getGlobalPrivateDnsHost(in ComponentName admin);
@@ -427,4 +428,10 @@
boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName);
List<String> getCrossProfileCalendarPackages(in ComponentName admin);
boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
+ List<String> getCrossProfileCalendarPackagesForUser(int userHandle);
+
+ boolean isManagedKiosk();
+ boolean isUnattendedManagedKiosk();
+
+ boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags);
}
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 5fee853..8b41755 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -16,8 +16,14 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,6 +41,8 @@
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
+ // TODO(b/120536847): refactor isActivePasswordSufficient logic so that the actual password
+ // quality is not overwritten
public int quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
public int length = 0;
public int letters = 0;
@@ -46,6 +54,10 @@
public PasswordMetrics() {}
+ public PasswordMetrics(int quality) {
+ this.quality = quality;
+ }
+
public PasswordMetrics(int quality, int length) {
this.quality = quality;
this.length = length;
@@ -173,6 +185,15 @@
&& this.nonLetter == o.nonLetter;
}
+ private boolean satisfiesBucket(PasswordMetrics... bucket) {
+ for (PasswordMetrics metrics : bucket) {
+ if (this.quality == metrics.quality) {
+ return this.length >= metrics.length;
+ }
+ }
+ return false;
+ }
+
/*
* Returns the maximum length of a sequential characters. A sequence is defined as
* monotonically increasing characters with a constant interval or the same character repeated.
@@ -254,4 +275,99 @@
return 0;
}
}
+
+ /** Determines the {@link PasswordComplexity} of this {@link PasswordMetrics}. */
+ @PasswordComplexity
+ public int determineComplexity() {
+ for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) {
+ if (satisfiesBucket(bucket.getMetrics())) {
+ return bucket.mComplexityLevel;
+ }
+ }
+ return PASSWORD_COMPLEXITY_NONE;
+ }
+
+ /**
+ * Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}.
+ */
+ public static class PasswordComplexityBucket {
+ /**
+ * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of
+ * {@link PasswordMetrics}.
+ */
+ private static final PasswordComplexityBucket HIGH =
+ new PasswordComplexityBucket(
+ PASSWORD_COMPLEXITY_HIGH,
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 6),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
+ 8));
+
+ /**
+ * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of
+ * {@link PasswordMetrics}.
+ */
+ private static final PasswordComplexityBucket MEDIUM =
+ new PasswordComplexityBucket(
+ PASSWORD_COMPLEXITY_MEDIUM,
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 4),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
+ 4));
+
+ /**
+ * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_LOW} in terms of
+ * {@link PasswordMetrics}.
+ */
+ private static final PasswordComplexityBucket LOW =
+ new PasswordComplexityBucket(
+ PASSWORD_COMPLEXITY_LOW,
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING));
+
+ /**
+ * A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}.
+ */
+ private static final PasswordComplexityBucket NONE =
+ new PasswordComplexityBucket(PASSWORD_COMPLEXITY_NONE, new PasswordMetrics());
+
+ /** Array containing all buckets from high to low. */
+ private static final PasswordComplexityBucket[] BUCKETS =
+ new PasswordComplexityBucket[] {HIGH, MEDIUM, LOW};
+
+ @PasswordComplexity
+ private final int mComplexityLevel;
+ private final PasswordMetrics[] mMetrics;
+
+ private PasswordComplexityBucket(@PasswordComplexity int complexityLevel,
+ PasswordMetrics... metrics) {
+ this.mComplexityLevel = complexityLevel;
+ this.mMetrics = metrics;
+ }
+
+ /** Returns the {@link PasswordMetrics} that meet the min requirements of this bucket. */
+ public PasswordMetrics[] getMetrics() {
+ return mMetrics;
+ }
+
+ /** Returns the bucket that {@code complexityLevel} represents. */
+ public static PasswordComplexityBucket complexityLevelToBucket(
+ @PasswordComplexity int complexityLevel) {
+ for (PasswordComplexityBucket bucket : BUCKETS) {
+ if (bucket.mComplexityLevel == complexityLevel) {
+ return bucket;
+ }
+ }
+ return NONE;
+ }
+ }
}
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index bcd5f6c..dd72845 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -689,13 +689,11 @@
mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
}
- @SystemApi
@Override
public int describeContents() {
return 0;
}
- @SystemApi
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mPolicyType);
@@ -712,7 +710,6 @@
}
}
- @SystemApi
public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
new Parcelable.Creator<SystemUpdatePolicy>() {
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 3e20748..0afb98f 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -91,6 +91,15 @@
* at some point in the future.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which backup service should be enabled/disabled.
+ */
+ void setBackupEnabledForUser(int userId, boolean isEnabled);
+
+ /**
+ * {@link android.app.backup.IBackupManager.setBackupEnabledForUser} for the calling user id.
*/
void setBackupEnabled(boolean isEnabled);
@@ -120,6 +129,15 @@
* Report whether the backup mechanism is currently enabled.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the backup service status should be reported.
+ */
+ boolean isBackupEnabledForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.isBackupEnabledForUser} for the calling user id.
*/
boolean isBackupEnabled();
@@ -149,6 +167,15 @@
* method.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an immediate backup should be scheduled.
+ */
+ void backupNowForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.backupNowForUser} for the calling user id.
*/
void backupNow();
@@ -432,6 +459,12 @@
* <p>If this method returns zero (meaning success), the OS will attempt to backup all provided
* packages using the remote transport.
*
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an immediate backup should be requested.
+
* @param observer The {@link BackupObserver} to receive callbacks during the backup
* operation.
*
@@ -442,12 +475,29 @@
*
* @return Zero on success; nonzero on error.
*/
+ int requestBackupForUser(int userId, in String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags);
+
+ /**
+ * {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id.
+ */
int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor,
int flags);
/**
* Cancel all running backups. After this call returns, no currently running backups will
* interact with the selected transport.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which backups should be cancelled.
+ */
+ void cancelBackupsForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.cancelBackups} for the calling user id.
*/
void cancelBackups();
}
diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java
index 2e0f940..79925ec 100644
--- a/core/java/android/app/backup/RestoreSession.java
+++ b/core/java/android/app/backup/RestoreSession.java
@@ -143,8 +143,6 @@
* @param packages The set of packages for which to attempt a restore. Regardless of
* the contents of the actual back-end dataset named by {@code token}, only
* applications mentioned in this list will have their data restored.
- *
- * @hide
*/
public int restoreSome(long token, RestoreObserver observer, BackupManagerMonitor monitor,
String[] packages) {
@@ -181,8 +179,6 @@
* @param packages The set of packages for which to attempt a restore. Regardless of
* the contents of the actual back-end dataset named by {@code token}, only
* applications mentioned in this list will have their data restored.
- *
- * @hide
*/
public int restoreSome(long token, RestoreObserver observer, String[] packages) {
return restoreSome(token, observer, null, packages);
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 556ffa24..e84517d 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -200,12 +200,23 @@
public static final int PRIORITY_SYNC_INITIALIZATION = 20;
/**
- * Value of {@link #getPriority} for a foreground app (overrides the supplied
+ * Value of {@link #getPriority} for a BFGS app (overrides the supplied
+ * JobInfo priority if it is smaller).
+ * @hide
+ */
+ public static final int PRIORITY_BOUND_FOREGROUND_SERVICE = 30;
+
+ /** @hide For backward compatibility. */
+ @UnsupportedAppUsage
+ public static final int PRIORITY_FOREGROUND_APP = PRIORITY_BOUND_FOREGROUND_SERVICE;
+
+ /**
+ * Value of {@link #getPriority} for a FG service app (overrides the supplied
* JobInfo priority if it is smaller).
* @hide
*/
@UnsupportedAppUsage
- public static final int PRIORITY_FOREGROUND_APP = 30;
+ public static final int PRIORITY_FOREGROUND_SERVICE = 35;
/**
* Value of {@link #getPriority} for the current top app (overrides the supplied
@@ -1593,4 +1604,29 @@
return new JobInfo(this);
}
}
+
+ /**
+ * Convert a priority integer into a human readable string for debugging.
+ * @hide
+ */
+ public static String getPriorityString(int priority) {
+ switch (priority) {
+ case PRIORITY_DEFAULT:
+ return PRIORITY_DEFAULT + " [DEFAULT]";
+ case PRIORITY_SYNC_EXPEDITED:
+ return PRIORITY_SYNC_EXPEDITED + " [SYNC_EXPEDITED]";
+ case PRIORITY_SYNC_INITIALIZATION:
+ return PRIORITY_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]";
+ case PRIORITY_BOUND_FOREGROUND_SERVICE:
+ return PRIORITY_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]";
+ case PRIORITY_FOREGROUND_SERVICE:
+ return PRIORITY_FOREGROUND_SERVICE + " [FGS_APP]";
+ case PRIORITY_TOP_APP:
+ return PRIORITY_TOP_APP + " [TOP_APP]";
+
+ // PRIORITY_ADJ_* are adjustments and not used as real priorities.
+ // No need to convert to strings.
+ }
+ return priority + " [UNKNOWN]";
+ }
}
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 578a9ae..3cc56ae 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -47,6 +47,8 @@
public static final int REASON_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
/** @hide */
public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
+ /** @hide */
+ public static final int REASON_DEVICE_THERMAL = JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5.
/** @hide */
public static String getReasonName(int reason) {
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl
similarity index 79%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to core/java/android/app/role/IOnRoleHoldersChangedListener.aidl
index c66a6cb..6cf961f 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package android.app.role;
-parcelable ContentCaptureEvent;
+/**
+ * @hide
+ */
+oneway interface IOnRoleHoldersChangedListener {
+
+ void onRoleHoldersChanged(String roleName, int userId);
+}
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 3ca8ec0..4ce0f31 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -16,6 +16,7 @@
package android.app.role;
+import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManagerCallback;
/**
@@ -37,6 +38,11 @@
void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
+ void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId);
+
+ void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener,
+ int userId);
+
void setRoleNamesFromController(in List<String> roleNames);
boolean addRoleHolderFromController(in String roleName, in String packageName);
diff --git a/core/java/android/app/role/OnRoleHoldersChangedListener.java b/core/java/android/app/role/OnRoleHoldersChangedListener.java
new file mode 100644
index 0000000..5958deb
--- /dev/null
+++ b/core/java/android/app/role/OnRoleHoldersChangedListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.UserHandle;
+
+/**
+ * Listener for role holder changes.
+ *
+ * @hide
+ */
+@SystemApi
+public interface OnRoleHoldersChangedListener {
+
+ /**
+ * Called when the holders of roles are changed.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param user the user for this role holder change
+ */
+ void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user);
+}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 7cb245a..5d101ab 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -22,6 +22,8 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -29,8 +31,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.util.List;
import java.util.concurrent.Executor;
@@ -124,6 +130,13 @@
@NonNull
private final IRoleManager mService;
+ @GuardedBy("mListenersLock")
+ @NonNull
+ private final SparseArray<ArrayMap<OnRoleHoldersChangedListener,
+ OnRoleHoldersChangedListenerDelegate>> mListeners = new SparseArray<>();
+ @NonNull
+ private final Object mListenersLock = new Object();
+
/**
* @hide
*/
@@ -145,8 +158,6 @@
* @param roleName the name of requested role
*
* @return the {@code Intent} to prompt user to grant the role
- *
- * @throws IllegalArgumentException if {@code role} is {@code null} or empty
*/
@NonNull
public Intent createRequestRoleIntent(@NonNull String roleName) {
@@ -163,8 +174,6 @@
* @param roleName the name of role to checking for
*
* @return whether the role is available in the system
- *
- * @throws IllegalArgumentException if the role name is {@code null} or empty
*/
public boolean isRoleAvailable(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -181,8 +190,6 @@
* @param roleName the name of the role to check for
*
* @return whether the calling application is holding the role
- *
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
*/
public boolean isRoleHeld(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -203,8 +210,6 @@
*
* @return a list of package names of the role holders, or an empty list if none.
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
*
* @hide
@@ -212,6 +217,7 @@
@NonNull
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public List<String> getRoleHolders(@NonNull String roleName) {
return getRoleHoldersAsUser(roleName, Process.myUserHandle());
}
@@ -228,8 +234,6 @@
*
* @return a list of package names of the role holders, or an empty list if none.
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -239,6 +243,7 @@
@NonNull
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkNotNull(user, "user cannot be null");
@@ -263,8 +268,6 @@
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -273,6 +276,7 @@
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
@NonNull RoleManagerCallback callback) {
@@ -302,8 +306,6 @@
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -312,6 +314,7 @@
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
@NonNull RoleManagerCallback callback) {
@@ -340,8 +343,6 @@
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
@@ -350,6 +351,7 @@
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user,
@CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -365,6 +367,96 @@
}
/**
+ * Add a listener to observe role holder changes
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+ *
+ * @param executor the {@code Executor} to call the listener on.
+ * @param listener the listener to be added
+ * @param user the user to add the listener for
+ *
+ * @see #removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener, UserHandle)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
+ @SystemApi
+ public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor,
+ @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
+ Preconditions.checkNotNull(executor, "executor cannot be null");
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(user, "user cannot be null");
+ int userId = user.getIdentifier();
+ synchronized (mListenersLock) {
+ ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
+ mListeners.get(userId);
+ if (listeners == null) {
+ listeners = new ArrayMap<>();
+ mListeners.put(userId, listeners);
+ } else {
+ if (listeners.containsKey(listener)) {
+ return;
+ }
+ }
+ OnRoleHoldersChangedListenerDelegate listenerDelegate =
+ new OnRoleHoldersChangedListenerDelegate(executor, listener);
+ try {
+ mService.addOnRoleHoldersChangedListenerAsUser(listenerDelegate, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ listeners.put(listener, listenerDelegate);
+ }
+ }
+
+ /**
+ * Remove a listener observing role holder changes
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+ *
+ * @param listener the listener to be removed
+ * @param user the user to remove the listener for
+ *
+ * @see #addOnRoleHoldersChangedListenerAsUser(Executor, OnRoleHoldersChangedListener,
+ * UserHandle)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
+ @SystemApi
+ public void removeOnRoleHoldersChangedListenerAsUser(
+ @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(user, "user cannot be null");
+ int userId = user.getIdentifier();
+ synchronized (mListenersLock) {
+ ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
+ mListeners.get(userId);
+ if (listeners == null) {
+ return;
+ }
+ OnRoleHoldersChangedListenerDelegate listenerDelegate = listeners.get(listener);
+ if (listenerDelegate == null) {
+ return;
+ }
+ try {
+ mService.removeOnRoleHoldersChangedListenerAsUser(listenerDelegate,
+ user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ listeners.remove(listener);
+ if (listeners.isEmpty()) {
+ mListeners.remove(userId);
+ }
+ }
+ }
+
+ /**
* Set the names of all the available roles. Should only be called from
* {@link android.rolecontrollerservice.RoleControllerService}.
* <p>
@@ -373,8 +465,6 @@
*
* @param roleNames the names of all the available roles
*
- * @throws IllegalArgumentException if the list of role names is {@code null}.
- *
* @hide
*/
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@@ -402,8 +492,6 @@
* @return whether the operation was successful, and will also be {@code true} if a matching
* role holder is already found.
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHolders(String)
* @see #removeRoleHolderFromController(String, String)
*
@@ -436,8 +524,6 @@
* @return whether the operation was successful, and will also be {@code true} if no matching
* role holder was found to remove.
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHolders(String)
* @see #addRoleHolderFromController(String, String)
*
@@ -489,4 +575,31 @@
}
}
}
+
+ private static class OnRoleHoldersChangedListenerDelegate
+ extends IOnRoleHoldersChangedListener.Stub {
+
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final OnRoleHoldersChangedListener mListener;
+
+ OnRoleHoldersChangedListenerDelegate(@NonNull Executor executor,
+ @NonNull OnRoleHoldersChangedListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(PooledLambda.obtainRunnable(
+ OnRoleHoldersChangedListener::onRoleHoldersChanged, mListener, roleName,
+ UserHandle.of(userId)));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/role/RoleManagerCallback.java b/core/java/android/app/role/RoleManagerCallback.java
index ca68ebc..d9f0a6c 100644
--- a/core/java/android/app/role/RoleManagerCallback.java
+++ b/core/java/android/app/role/RoleManagerCallback.java
@@ -17,6 +17,7 @@
package android.app.role;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
/**
* Callback for a {@link RoleManager} request.
@@ -24,6 +25,7 @@
* @hide
*/
@SystemApi
+@TestApi
public interface RoleManagerCallback {
/**
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
index aaae57e5..a79ad2f 100644
--- a/core/java/android/app/usage/EventList.java
+++ b/core/java/android/app/usage/EventList.java
@@ -103,4 +103,21 @@
}
return result;
}
+
+ /**
+ * Remove events of certain type on or after a timestamp.
+ * @param type The type of event to remove.
+ * @param timeStamp the timeStamp on or after which to remove the event.
+ */
+ public void removeOnOrAfter(int type, long timeStamp) {
+ for (int i = mEvents.size() - 1; i >= 0; i--) {
+ UsageEvents.Event event = mEvents.get(i);
+ if (event.mTimeStamp < timeStamp) {
+ break;
+ }
+ if (event.mEventType == type) {
+ mEvents.remove(i);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 3a5975a..a06213d 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -50,13 +50,27 @@
public static final int NONE = 0;
/**
+ * @deprecated by {@link #ACTIVITY_RESUMED}
+ */
+ @Deprecated
+ public static final int MOVE_TO_FOREGROUND = 1;
+
+ /**
* An event type denoting that an {@link android.app.Activity} moved to the foreground.
* This event has a package name and class name associated with it and can be retrieved
* using {@link #getPackageName()} and {@link #getClassName()}.
* If a package has multiple activities, this event is reported for each activity that moves
* to foreground.
+ * This event is corresponding to {@link android.app.Activity#onResume()} of the
+ * activity's lifecycle.
*/
- public static final int MOVE_TO_FOREGROUND = 1;
+ public static final int ACTIVITY_RESUMED = MOVE_TO_FOREGROUND;
+
+ /**
+ * @deprecated by {@link #ACTIVITY_PAUSED}
+ */
+ @Deprecated
+ public static final int MOVE_TO_BACKGROUND = 2;
/**
* An event type denoting that an {@link android.app.Activity} moved to the background.
@@ -64,19 +78,21 @@
* using {@link #getPackageName()} and {@link #getClassName()}.
* If a package has multiple activities, this event is reported for each activity that moves
* to background.
+ * This event is corresponding to {@link android.app.Activity#onPause()} of the activity's
+ * lifecycle.
*/
- public static final int MOVE_TO_BACKGROUND = 2;
+ public static final int ACTIVITY_PAUSED = MOVE_TO_BACKGROUND;
/**
* An event type denoting that a component was in the foreground when the stats
- * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}.
+ * rolled-over. This is effectively treated as a {@link #ACTIVITY_PAUSED}.
* {@hide}
*/
public static final int END_OF_DAY = 3;
/**
* An event type denoting that a component was in the foreground the previous day.
- * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}.
+ * This is effectively treated as a {@link #ACTIVITY_RESUMED}.
* {@hide}
*/
public static final int CONTINUE_PREVIOUS_DAY = 4;
@@ -207,10 +223,31 @@
public static final int ROLLOVER_FOREGROUND_SERVICE = 22;
/**
+ * An activity becomes invisible on the UI, corresponding to
+ * {@link android.app.Activity#onStop()} of the activity's lifecycle.
+ */
+ public static final int ACTIVITY_STOPPED = 23;
+
+ /**
+ * An activity object is destroyed, corresponding to
+ * {@link android.app.Activity#onDestroy()} of the activity's lifecycle.
+ * {@hide}
+ */
+ public static final int ACTIVITY_DESTROYED = 24;
+
+ /**
+ * The event type demoting that a flush of UsageStatsDatabase to file system. Before the
+ * flush all usage stats need to be updated to latest timestamp to make sure the most
+ * up to date stats are persisted.
+ * @hide
+ */
+ public static final int FLUSH_TO_DISK = 25;
+
+ /**
* Keep in sync with the greatest event type value.
* @hide
*/
- public static final int MAX_EVENT_TYPE = 22;
+ public static final int MAX_EVENT_TYPE = 25;
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
@@ -240,6 +277,12 @@
@UnsupportedAppUsage
public String mClass;
+
+ /**
+ * {@hide}
+ */
+ public int mInstanceId;
+
/**
* {@hide}
*/
@@ -311,9 +354,16 @@
}
/** @hide */
+ public Event(int type, long timeStamp) {
+ mEventType = type;
+ mTimeStamp = timeStamp;
+ }
+
+ /** @hide */
public Event(Event orig) {
mPackage = orig.mPackage;
mClass = orig.mClass;
+ mInstanceId = orig.mInstanceId;
mTimeStamp = orig.mTimeStamp;
mEventType = orig.mEventType;
mConfiguration = orig.mConfiguration;
@@ -342,6 +392,16 @@
}
/**
+ * An activity can be instantiated multiple times, this is the unique activity instance ID.
+ * For non-activity class, instance ID is always zero.
+ * @hide
+ */
+ @SystemApi
+ public int getInstanceId() {
+ return mInstanceId;
+ }
+
+ /**
* The time at which this event occurred, measured in milliseconds since the epoch.
* <p/>
* See {@link System#currentTimeMillis()}.
@@ -352,12 +412,14 @@
/**
* The event type.
- *
- * @see #MOVE_TO_BACKGROUND
- * @see #MOVE_TO_FOREGROUND
+ * @see #ACTIVITY_PAUSED
+ * @see #ACTIVITY_RESUMED
* @see #CONFIGURATION_CHANGE
* @see #USER_INTERACTION
* @see #STANDBY_BUCKET_CHANGED
+ * @see #FOREGROUND_SERVICE_START
+ * @see #FOREGROUND_SERVICE_STOP
+ * @see #ACTIVITY_STOPPED
*/
public int getEventType() {
return mEventType;
@@ -576,6 +638,7 @@
}
p.writeInt(packageIndex);
p.writeInt(classIndex);
+ p.writeInt(event.mInstanceId);
p.writeInt(event.mEventType);
p.writeLong(event.mTimeStamp);
@@ -618,6 +681,7 @@
} else {
eventOut.mClass = null;
}
+ eventOut.mInstanceId = p.readInt();
eventOut.mEventType = p.readInt();
eventOut.mTimeStamp = p.readLong();
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 73426e4..8fb7f4c 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -16,13 +16,15 @@
package android.app.usage;
-import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
import android.annotation.SystemApi;
@@ -31,6 +33,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.util.SparseIntArray;
/**
* Contains usage statistics for an app package for a specific
@@ -57,13 +60,20 @@
public long mEndTimeStamp;
/**
- * Last time used by the user with an explicit action (notification, activity launch)
+ * Last time an activity is at foreground (have focus), this is corresponding to
+ * {@link android.app.usage.UsageEvents.Event#ACTIVITY_RESUMED} event.
* {@hide}
*/
@UnsupportedAppUsage
public long mLastTimeUsed;
/**
+ * Last time an activity is visible.
+ * @hide
+ */
+ public long mLastTimeVisible;
+
+ /**
* Total time this package's activity is in foreground.
* {@hide}
*/
@@ -71,6 +81,12 @@
public long mTotalTimeInForeground;
/**
+ * Total time this package's activity is visible.
+ * {@hide}
+ */
+ public long mTotalTimeVisible;
+
+ /**
* Last time foreground service is started.
* {@hide}
*/
@@ -93,31 +109,32 @@
*/
public int mAppLaunchCount;
- /** Last activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event.
+ /** Last activity ACTIVITY_RESUMED or ACTIVITY_PAUSED event.
* {@hide}
- * @deprecated use {@link #mLastForegroundActivityEventMap} instead.
+ * @deprecated use {@link #mActivities} instead.
*/
@UnsupportedAppUsage
@Deprecated
public int mLastEvent;
/**
- * If an activity is in foreground, it has one entry in this map.
- * When activity moves to background, it is removed from this map.
- * Key is activity class name.
- * Value is last time this activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event.
+ * If an activity is visible(onStart(), onPause() states) or in foreground (onResume() state),
+ * it has one entry in this map. When an activity becomes invisible (onStop() or onDestroy()),
+ * it is removed from this map.
+ * Key is instanceId of the activity (ActivityRecode appToken hashCode)..
+ * Value is this activity's last event, one of ACTIVITY_RESUMED or
+ * ACTIVITY_PAUSED.
* {@hide}
*/
- public ArrayMap<String, Integer> mLastForegroundActivityEventMap = new ArrayMap<>();
-
+ public SparseIntArray mActivities = new SparseIntArray();
/**
* If a foreground service is started, it has one entry in this map.
- * When a foreground service is stopped, it is removed from this map.
+ * When a foreground service is stopped, it is removed from this set.
* Key is foreground service class name.
- * Value is last foreground service FOREGROUND_SERVICE_START ot FOREGROUND_SERVICE_STOP event.
+ * Value is the foreground service's last event, it is FOREGROUND_SERVICE_START.
* {@hide}
*/
- public ArrayMap<String, Integer> mLastForegroundServiceEventMap = new ArrayMap<>();
+ public ArrayMap<String, Integer> mForegroundServices = new ArrayMap<>();
/**
* {@hide}
@@ -135,14 +152,16 @@
mBeginTimeStamp = stats.mBeginTimeStamp;
mEndTimeStamp = stats.mEndTimeStamp;
mLastTimeUsed = stats.mLastTimeUsed;
+ mLastTimeVisible = stats.mLastTimeVisible;
mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed;
mTotalTimeInForeground = stats.mTotalTimeInForeground;
+ mTotalTimeVisible = stats.mTotalTimeVisible;
mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed;
mLaunchCount = stats.mLaunchCount;
mAppLaunchCount = stats.mAppLaunchCount;
mLastEvent = stats.mLastEvent;
- mLastForegroundActivityEventMap = stats.mLastForegroundActivityEventMap;
- mLastForegroundServiceEventMap = stats.mLastForegroundServiceEventMap;
+ mActivities = stats.mActivities;
+ mForegroundServices = stats.mForegroundServices;
mChooserCounts = stats.mChooserCounts;
}
@@ -191,6 +210,14 @@
}
/**
+ * Get the last time this package's activity is visible in the UI, measured in milliseconds
+ * since the epoch.
+ */
+ public long getLastTimeVisible() {
+ return mLastTimeVisible;
+ }
+
+ /**
* Get the total time this package spent in the foreground, measured in milliseconds.
*/
public long getTotalTimeInForeground() {
@@ -198,6 +225,13 @@
}
/**
+ * Get the total time this package's activity is visible in the UI, measured in milliseconds.
+ */
+ public long getTotalTimeVisible() {
+ return mTotalTimeVisible;
+ }
+
+ /**
* Get the last time this package's foreground service was used, measured in milliseconds since
* the epoch.
* <p/>
@@ -224,6 +258,20 @@
return mAppLaunchCount;
}
+ private void mergeEventMap(SparseIntArray left, SparseIntArray right) {
+ final int size = right.size();
+ for (int i = 0; i < size; i++) {
+ final int instanceId = right.keyAt(i);
+ final int event = right.valueAt(i);
+ final int index = left.indexOfKey(instanceId);
+ if (index >= 0) {
+ left.put(instanceId, Math.max(left.valueAt(index), event));
+ } else {
+ left.put(instanceId, event);
+ }
+ }
+ }
+
private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) {
final int size = right.size();
for (int i = 0; i < size; i++) {
@@ -255,15 +303,17 @@
if (right.mBeginTimeStamp > mBeginTimeStamp) {
// Even though incoming UsageStat begins after this one, its last time used fields
// may somehow be empty or chronologically preceding the older UsageStat.
- mergeEventMap(mLastForegroundActivityEventMap, right.mLastForegroundActivityEventMap);
- mergeEventMap(mLastForegroundServiceEventMap, right.mLastForegroundServiceEventMap);
+ mergeEventMap(mActivities, right.mActivities);
+ mergeEventMap(mForegroundServices, right.mForegroundServices);
mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
+ mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible);
mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed,
right.mLastTimeForegroundServiceUsed);
}
mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
mTotalTimeInForeground += right.mTotalTimeInForeground;
+ mTotalTimeVisible += right.mTotalTimeVisible;
mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed;
mLaunchCount += right.mLaunchCount;
mAppLaunchCount += right.mAppLaunchCount;
@@ -290,36 +340,76 @@
}
/**
- * Tell if an event indicate activity is in foreground or not.
- * @param event the activity event.
- * @return true if activity is in foreground, false otherwise.
- * @hide
+ * Tell if any activity is in foreground.
+ * @return
*/
- private boolean isActivityInForeground(int event) {
- return event == MOVE_TO_FOREGROUND
- || event == CONTINUE_PREVIOUS_DAY;
+ private boolean hasForegroundActivity() {
+ final int size = mActivities.size();
+ for (int i = 0; i < size; i++) {
+ if (mActivities.valueAt(i) == ACTIVITY_RESUMED) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * Tell if an event indicate foreground sevice is started or not.
- * @param event the foreground service event.
- * @return true if foreground service is started, false if stopped.
- * @hide
+ * Tell if any activity is visible.
+ * @return
*/
- private boolean isForegroundServiceStarted(int event) {
- return event == FOREGROUND_SERVICE_START
- || event == CONTINUING_FOREGROUND_SERVICE;
+ private boolean hasVisibleActivity() {
+ final int size = mActivities.size();
+ for (int i = 0; i < size; i++) {
+ final int type = mActivities.valueAt(i);
+ if (type == ACTIVITY_RESUMED
+ || type == ACTIVITY_PAUSED) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * If any activity in foreground or any foreground service is started, the app is considered in
- * use.
- * @return true if in use, false otherwise.
- * @hide
+ * Tell if any foreground service is started.
+ * @return
*/
- private boolean isAppInUse() {
- return !mLastForegroundActivityEventMap.isEmpty()
- || !mLastForegroundServiceEventMap.isEmpty();
+ private boolean anyForegroundServiceStarted() {
+ return !mForegroundServices.isEmpty();
+ }
+
+ /**
+ * Increment total time in foreground and update last time in foreground.
+ * @param timeStamp current timestamp.
+ */
+ private void incrementTimeUsed(long timeStamp) {
+ if (timeStamp > mLastTimeUsed) {
+ mTotalTimeInForeground += timeStamp - mLastTimeUsed;
+ mLastTimeUsed = timeStamp;
+ }
+ }
+
+ /**
+ * Increment total time visible and update last time visible.
+ * @param timeStamp current timestmap.
+ */
+ private void incrementTimeVisible(long timeStamp) {
+ if (timeStamp > mLastTimeVisible) {
+ mTotalTimeVisible += timeStamp - mLastTimeVisible;
+ mLastTimeVisible = timeStamp;
+ }
+ }
+
+ /**
+ * Increment total time foreground service is used and update last time foreground service is
+ * used.
+ * @param timeStamp current timestamp.
+ */
+ private void incrementServiceTimeUsed(long timeStamp) {
+ if (timeStamp > mLastTimeForegroundServiceUsed) {
+ mTotalTimeForegroundServiceUsed +=
+ timeStamp - mLastTimeForegroundServiceUsed;
+ mLastTimeForegroundServiceUsed = timeStamp;
+ }
}
/**
@@ -327,33 +417,63 @@
* @param className className of the activity.
* @param timeStamp timeStamp of the event.
* @param eventType type of the event.
+ * @param instanceId hashCode of the ActivityRecord's appToken.
* @hide
*/
- private void updateForegroundActivity(String className, long timeStamp, int eventType) {
- if (eventType != MOVE_TO_BACKGROUND
- && eventType != MOVE_TO_FOREGROUND
- && eventType != END_OF_DAY) {
+ private void updateActivity(String className, long timeStamp, int eventType, int instanceId) {
+ if (eventType != ACTIVITY_RESUMED
+ && eventType != ACTIVITY_PAUSED
+ && eventType != ACTIVITY_STOPPED
+ && eventType != ACTIVITY_DESTROYED) {
return;
}
- final Integer lastEvent = mLastForegroundActivityEventMap.get(className);
- if (lastEvent != null) {
- if (isActivityInForeground(lastEvent)) {
- if (timeStamp > mLastTimeUsed) {
- mTotalTimeInForeground += timeStamp - mLastTimeUsed;
+ // update usage.
+ final int index = mActivities.indexOfKey(instanceId);
+ if (index >= 0) {
+ final int lastEvent = mActivities.valueAt(index);
+ switch (lastEvent) {
+ case ACTIVITY_RESUMED:
+ incrementTimeUsed(timeStamp);
+ incrementTimeVisible(timeStamp);
+ break;
+ case ACTIVITY_PAUSED:
+ incrementTimeVisible(timeStamp);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // update current event.
+ switch(eventType) {
+ case ACTIVITY_RESUMED:
+ if (!hasVisibleActivity()) {
+ // this is the first visible activity.
+ mLastTimeUsed = timeStamp;
+ mLastTimeVisible = timeStamp;
+ } else if (!hasForegroundActivity()) {
+ // this is the first foreground activity.
mLastTimeUsed = timeStamp;
}
- }
- if (eventType == MOVE_TO_BACKGROUND) {
- mLastForegroundActivityEventMap.remove(className);
- } else {
- mLastForegroundActivityEventMap.put(className, eventType);
- }
- } else if (eventType == MOVE_TO_FOREGROUND) {
- if (!isAppInUse()) {
- mLastTimeUsed = timeStamp;
- }
- mLastForegroundActivityEventMap.put(className, eventType);
+ mActivities.put(instanceId, eventType);
+ break;
+ case ACTIVITY_PAUSED:
+ if (!hasVisibleActivity()) {
+ // this is the first visible activity.
+ mLastTimeVisible = timeStamp;
+ }
+ mActivities.put(instanceId, eventType);
+ break;
+ case ACTIVITY_STOPPED:
+ mActivities.put(instanceId, eventType);
+ break;
+ case ACTIVITY_DESTROYED:
+ // remove activity from the map.
+ mActivities.delete(instanceId);
+ break;
+ default:
+ break;
}
}
@@ -366,80 +486,97 @@
*/
private void updateForegroundService(String className, long timeStamp, int eventType) {
if (eventType != FOREGROUND_SERVICE_STOP
- && eventType != FOREGROUND_SERVICE_START
- && eventType != ROLLOVER_FOREGROUND_SERVICE) {
+ && eventType != FOREGROUND_SERVICE_START) {
return;
}
- final Integer lastEvent = mLastForegroundServiceEventMap.get(className);
+ final Integer lastEvent = mForegroundServices.get(className);
+ // update usage.
if (lastEvent != null) {
- if (isForegroundServiceStarted(lastEvent)) {
- if (timeStamp > mLastTimeForegroundServiceUsed) {
- mTotalTimeForegroundServiceUsed +=
- timeStamp - mLastTimeForegroundServiceUsed;
+ switch (lastEvent) {
+ case FOREGROUND_SERVICE_START:
+ case CONTINUING_FOREGROUND_SERVICE:
+ incrementServiceTimeUsed(timeStamp);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // update current event.
+ switch (eventType) {
+ case FOREGROUND_SERVICE_START:
+ if (!anyForegroundServiceStarted()) {
mLastTimeForegroundServiceUsed = timeStamp;
}
- }
- if (eventType == FOREGROUND_SERVICE_STOP) {
- mLastForegroundServiceEventMap.remove(className);
- } else {
- mLastForegroundServiceEventMap.put(className, eventType);
- }
- } else if (eventType == FOREGROUND_SERVICE_START) {
- if (!isAppInUse()) {
- mLastTimeForegroundServiceUsed = timeStamp;
- }
- mLastForegroundServiceEventMap.put(className, eventType);
+ mForegroundServices.put(className, eventType);
+ break;
+ case FOREGROUND_SERVICE_STOP:
+ mForegroundServices.remove(className);
+ break;
+ default:
+ break;
}
}
/**
* Update the UsageStats by a activity or foreground service event.
- * @param className class name of a activity or foreground service, could be null to mark
- * END_OF_DAY or rollover.
+ * @param className class name of a activity or foreground service, could be null to if this
+ * is sent to all activities/services in this package.
* @param timeStamp Epoch timestamp in milliseconds.
* @param eventType event type as in {@link UsageEvents.Event}
+ * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken.
+ * if className is not an activity, instanceId is not used.
* @hide
*/
- public void update(String className, long timeStamp, int eventType) {
+ public void update(String className, long timeStamp, int eventType, int instanceId) {
switch(eventType) {
- case MOVE_TO_BACKGROUND:
- case MOVE_TO_FOREGROUND:
- updateForegroundActivity(className, timeStamp, eventType);
+ case ACTIVITY_RESUMED:
+ case ACTIVITY_PAUSED:
+ case ACTIVITY_STOPPED:
+ case ACTIVITY_DESTROYED:
+ updateActivity(className, timeStamp, eventType, instanceId);
break;
case END_OF_DAY:
- // END_OF_DAY means updating all activities.
- final int size = mLastForegroundActivityEventMap.size();
- for (int i = 0; i < size; i++) {
- final String name = mLastForegroundActivityEventMap.keyAt(i);
- updateForegroundActivity(name, timeStamp, eventType);
+ // END_OF_DAY updates all activities.
+ if (hasForegroundActivity()) {
+ incrementTimeUsed(timeStamp);
+ }
+ if (hasVisibleActivity()) {
+ incrementTimeVisible(timeStamp);
}
break;
- case CONTINUE_PREVIOUS_DAY:
- mLastTimeUsed = timeStamp;
- mLastForegroundActivityEventMap.put(className, eventType);
- break;
- case FOREGROUND_SERVICE_STOP:
case FOREGROUND_SERVICE_START:
+ case FOREGROUND_SERVICE_STOP:
updateForegroundService(className, timeStamp, eventType);
break;
case ROLLOVER_FOREGROUND_SERVICE:
- // ROLLOVER_FOREGROUND_SERVICE means updating all foreground services.
- final int size2 = mLastForegroundServiceEventMap.size();
- for (int i = 0; i < size2; i++) {
- final String name = mLastForegroundServiceEventMap.keyAt(i);
- updateForegroundService(name, timeStamp, eventType);
+ // ROLLOVER_FOREGROUND_SERVICE updates all foreground services.
+ if (anyForegroundServiceStarted()) {
+ incrementServiceTimeUsed(timeStamp);
}
break;
case CONTINUING_FOREGROUND_SERVICE:
mLastTimeForegroundServiceUsed = timeStamp;
- mLastForegroundServiceEventMap.put(className, eventType);
+ mForegroundServices.put(className, eventType);
+ break;
+ case FLUSH_TO_DISK:
+ // update usage of all active activities/services.
+ if (hasForegroundActivity()) {
+ incrementTimeUsed(timeStamp);
+ }
+ if (hasVisibleActivity()) {
+ incrementTimeVisible(timeStamp);
+ }
+ if (anyForegroundServiceStarted()) {
+ incrementServiceTimeUsed(timeStamp);
+ }
break;
default:
break;
}
mEndTimeStamp = timeStamp;
- if (eventType == MOVE_TO_FOREGROUND) {
+ if (eventType == ACTIVITY_RESUMED) {
mLaunchCount += 1;
}
}
@@ -455,8 +592,10 @@
dest.writeLong(mBeginTimeStamp);
dest.writeLong(mEndTimeStamp);
dest.writeLong(mLastTimeUsed);
+ dest.writeLong(mLastTimeVisible);
dest.writeLong(mLastTimeForegroundServiceUsed);
dest.writeLong(mTotalTimeInForeground);
+ dest.writeLong(mTotalTimeVisible);
dest.writeLong(mTotalTimeForegroundServiceUsed);
dest.writeInt(mLaunchCount);
dest.writeInt(mAppLaunchCount);
@@ -477,21 +616,26 @@
}
dest.writeBundle(allCounts);
- final Bundle foregroundActivityEventBundle = new Bundle();
- final int foregroundEventSize = mLastForegroundActivityEventMap.size();
- for (int i = 0; i < foregroundEventSize; i++) {
- foregroundActivityEventBundle.putInt(mLastForegroundActivityEventMap.keyAt(i),
- mLastForegroundActivityEventMap.valueAt(i));
- }
- dest.writeBundle(foregroundActivityEventBundle);
+ writeSparseIntArray(dest, mActivities);
+ dest.writeBundle(eventMapToBundle(mForegroundServices));
+ }
- final Bundle foregroundServiceEventBundle = new Bundle();
- final int foregroundServiceEventSize = mLastForegroundServiceEventMap.size();
- for (int i = 0; i < foregroundServiceEventSize; i++) {
- foregroundServiceEventBundle.putInt(mLastForegroundServiceEventMap.keyAt(i),
- mLastForegroundServiceEventMap.valueAt(i));
+ private void writeSparseIntArray(Parcel dest, SparseIntArray arr) {
+ final int size = arr.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ dest.writeInt(arr.keyAt(i));
+ dest.writeInt(arr.valueAt(i));
}
- dest.writeBundle(foregroundServiceEventBundle);
+ }
+
+ private Bundle eventMapToBundle(ArrayMap<String, Integer> eventMap) {
+ final Bundle bundle = new Bundle();
+ final int size = eventMap.size();
+ for (int i = 0; i < size; i++) {
+ bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i));
+ }
+ return bundle;
}
public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -502,8 +646,10 @@
stats.mBeginTimeStamp = in.readLong();
stats.mEndTimeStamp = in.readLong();
stats.mLastTimeUsed = in.readLong();
+ stats.mLastTimeVisible = in.readLong();
stats.mLastTimeForegroundServiceUsed = in.readLong();
stats.mTotalTimeInForeground = in.readLong();
+ stats.mTotalTimeVisible = in.readLong();
stats.mTotalTimeForegroundServiceUsed = in.readLong();
stats.mLaunchCount = in.readInt();
stats.mAppLaunchCount = in.readInt();
@@ -527,12 +673,21 @@
}
}
}
- readBundleToEventMap(stats.mLastForegroundActivityEventMap, in.readBundle());
- readBundleToEventMap(stats.mLastForegroundServiceEventMap, in.readBundle());
+ readSparseIntArray(in, stats.mActivities);
+ readBundleToEventMap(in.readBundle(), stats.mForegroundServices);
return stats;
}
- private void readBundleToEventMap(ArrayMap<String, Integer> eventMap, Bundle bundle) {
+ private void readSparseIntArray(Parcel in, SparseIntArray arr) {
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final int key = in.readInt();
+ final int value = in.readInt();
+ arr.put(key, value);
+ }
+ }
+
+ private void readBundleToEventMap(Bundle bundle, ArrayMap<String, Integer> eventMap) {
if (bundle != null) {
for (String className : bundle.keySet()) {
final int event = bundle.getInt(className);
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 1a656ab..2edad35 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -37,9 +37,12 @@
* @param component The component for which this event occurred.
* @param userId The user id to which the component belongs to.
* @param eventType The event that occurred. Valid values can be found at
- * {@link UsageEvents}
+ * {@link UsageEvents}
+ * @param instanceId For activity, hashCode of ActivityRecord's appToken.
+ * For non-activity, it is not used.
*/
- public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType);
+ public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType,
+ int instanceId);
/**
* Reports an event to the UsageStatsManager.
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 318dbee..c740c42 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -88,6 +88,7 @@
int mViewMode = VIEW_MODE_NOINIT;
int mLayoutId = -1;
private OnClickHandler mOnClickHandler;
+ private boolean mOnLightBackground;
private Executor mAsyncExecutor;
private CancellationSignal mLastExecutionSignal;
@@ -374,6 +375,15 @@
}
/**
+ * Sets whether the widget should is being displayed on a light/white background and use an
+ * alternate UI if available.
+ * @see RemoteViews#setLightBackgroundLayoutId(int)
+ */
+ public void setOnLightBackground(boolean useDarkTextLayout) {
+ mOnLightBackground = useDarkTextLayout;
+ }
+
+ /**
* Update the AppWidgetProviderInfo for this view, and reset it to the
* initial layout.
*/
@@ -413,6 +423,10 @@
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
+ if (mOnLightBackground) {
+ remoteViews = remoteViews.getDarkTextViews();
+ }
+
if (mAsyncExecutor != null && useAsyncIfPossible) {
inflateAsync(remoteViews);
return;
diff --git a/core/java/android/content/ContentInterface.java b/core/java/android/content/ContentInterface.java
new file mode 100644
index 0000000..3d732eb
--- /dev/null
+++ b/core/java/android/content/ContentInterface.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * Interface representing calls that can be made to {@link ContentProvider}
+ * instances.
+ * <p>
+ * These methods have been extracted into a general interface so that APIs can
+ * be flexible in accepting either a {@link ContentProvider}, a
+ * {@link ContentResolver}, or a {@link ContentProviderClient}.
+ */
+public interface ContentInterface {
+ public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
+ throws RemoteException;
+
+ public @Nullable String getType(@NonNull Uri uri) throws RemoteException;
+
+ public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter)
+ throws RemoteException;
+
+ public @Nullable Uri canonicalize(@NonNull Uri uri) throws RemoteException;
+
+ public @Nullable Uri uncanonicalize(@NonNull Uri uri) throws RemoteException;
+
+ public boolean refresh(@NonNull Uri uri, @Nullable Bundle args,
+ @Nullable CancellationSignal cancellationSignal) throws RemoteException;
+
+ public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues)
+ throws RemoteException;
+
+ public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] initialValues)
+ throws RemoteException;
+
+ public int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) throws RemoteException;
+
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+ @Nullable String[] selectionArgs) throws RemoteException;
+
+ public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+ @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+ public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+ @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+ public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+ @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+ @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+ public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+ @NonNull ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException;
+
+ public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+ @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 145c9273..5a12e4e 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -105,7 +105,7 @@
* developer guide.</p>
* </div>
*/
-public abstract class ContentProvider implements ComponentCallbacks2 {
+public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {
private static final String TAG = "ContentProvider";
@@ -324,7 +324,7 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg,
+ public ContentProviderResult[] applyBatch(String callingPkg, String authority,
ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
int numOperations = operations.size();
@@ -356,7 +356,8 @@
Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
final String original = setCallingPackage(callingPkg);
try {
- ContentProviderResult[] results = ContentProvider.this.applyBatch(operations);
+ ContentProviderResult[] results = ContentProvider.this.applyBatch(authority,
+ operations);
if (results != null) {
for (int i = 0; i < results.length ; i++) {
if (userIds[i] != UserHandle.USER_CURRENT) {
@@ -444,13 +445,13 @@
}
@Override
- public Bundle call(
- String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras) {
+ public Bundle call(String callingPkg, String authority, String method, @Nullable String arg,
+ @Nullable Bundle extras) {
Bundle.setDefusable(extras, true);
Trace.traceBegin(TRACE_TAG_DATABASE, "call");
final String original = setCallingPackage(callingPkg);
try {
- return ContentProvider.this.call(method, arg, extras);
+ return ContentProvider.this.call(authority, method, arg, extras);
} finally {
setCallingPackage(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
@@ -586,7 +587,13 @@
private int noteProxyOp(String callingPkg, int op) {
if (op != AppOpsManager.OP_NONE) {
int mode = mAppOpsManager.noteProxyOp(op, callingPkg);
- return mode == MODE_DEFAULT ? interpretDefaultAppOpMode(op) : mode;
+ int nonDefaultMode = mode == MODE_DEFAULT ? interpretDefaultAppOpMode(op) : mode;
+ if (mode == MODE_DEFAULT && nonDefaultMode == MODE_IGNORED) {
+ Log.w(TAG, "Denying access for " + callingPkg + " to " + getClass().getName()
+ + " (" + AppOpsManager.opToName(op)
+ + " = " + AppOpsManager.opToName(mode) + ")");
+ }
+ return mode == MODE_DEFAULT ? nonDefaultMode : mode;
}
return AppOpsManager.MODE_ALLOWED;
@@ -1249,6 +1256,7 @@
* or {@code null}.
* @return a Cursor or {@code null}.
*/
+ @Override
public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;
@@ -1287,6 +1295,7 @@
* @param uri the URI to query.
* @return a MIME type string, or {@code null} if there is no type.
*/
+ @Override
public abstract @Nullable String getType(@NonNull Uri uri);
/**
@@ -1319,6 +1328,7 @@
* @return Return the canonical representation of <var>url</var>, or null if
* canonicalization of that Uri is not supported.
*/
+ @Override
public @Nullable Uri canonicalize(@NonNull Uri url) {
return null;
}
@@ -1337,6 +1347,7 @@
* the data identified by the canonical representation can not be found in
* the current environment.
*/
+ @Override
public @Nullable Uri uncanonicalize(@NonNull Uri url) {
return url;
}
@@ -1363,6 +1374,7 @@
* canceled the refresh request.
* @return true if the provider actually tried refreshing.
*/
+ @Override
public boolean refresh(Uri uri, @Nullable Bundle args,
@Nullable CancellationSignal cancellationSignal) {
return false;
@@ -1397,6 +1409,7 @@
* This must not be {@code null}.
* @return The URI for the newly inserted item.
*/
+ @Override
public abstract @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values);
/**
@@ -1414,6 +1427,7 @@
* This must not be {@code null}.
* @return The number of values that were inserted.
*/
+ @Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
int numValues = values.length;
for (int i = 0; i < numValues; i++) {
@@ -1442,6 +1456,7 @@
* @return The number of rows affected.
* @throws SQLException
*/
+ @Override
public abstract int delete(@NonNull Uri uri, @Nullable String selection,
@Nullable String[] selectionArgs);
@@ -1462,6 +1477,7 @@
* @param selection An optional filter to match rows to update.
* @return the number of rows affected.
*/
+ @Override
public abstract int update(@NonNull Uri uri, @Nullable ContentValues values,
@Nullable String selection, @Nullable String[] selectionArgs);
@@ -1598,6 +1614,7 @@
* @see #getType(android.net.Uri)
* @see ParcelFileDescriptor#parseMode(String)
*/
+ @Override
public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
@Nullable CancellationSignal signal) throws FileNotFoundException {
return openFile(uri, mode);
@@ -1717,6 +1734,7 @@
* @see #openFileHelper(Uri, String)
* @see #getType(android.net.Uri)
*/
+ @Override
public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
@Nullable CancellationSignal signal) throws FileNotFoundException {
return openAssetFile(uri, mode);
@@ -1783,6 +1801,7 @@
* @see #openTypedAssetFile(Uri, String, Bundle)
* @see ClipDescription#compareMimeTypes(String, String)
*/
+ @Override
public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter) {
return null;
}
@@ -1899,6 +1918,7 @@
* @see #openAssetFile(Uri, String)
* @see ClipDescription#compareMimeTypes(String, String)
*/
+ @Override
public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
@NonNull String mimeTypeFilter, @Nullable Bundle opts,
@Nullable CancellationSignal signal) throws FileNotFoundException {
@@ -2053,6 +2073,13 @@
* @throws OperationApplicationException thrown if any operation fails.
* @see ContentProviderOperation#apply
*/
+ @Override
+ public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+ @NonNull ArrayList<ContentProviderOperation> operations)
+ throws OperationApplicationException {
+ return applyBatch(operations);
+ }
+
public @NonNull ContentProviderResult[] applyBatch(
@NonNull ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
@@ -2082,6 +2109,12 @@
* @return provider-defined return value. May be {@code null}, which is also
* the default for providers which don't implement any call methods.
*/
+ @Override
+ public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+ @Nullable String arg, @Nullable Bundle extras) {
+ return call(method, arg, extras);
+ }
+
public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
@Nullable Bundle extras) {
return null;
@@ -2127,6 +2160,19 @@
writer.println("nothing to dump");
}
+ private void validateIncomingAuthority(String authority) throws SecurityException {
+ if (!matchesOurAuthorities(getAuthorityWithoutUserId(authority))) {
+ String message = "The authority " + authority + " does not match the one of the "
+ + "contentProvider: ";
+ if (mAuthority != null) {
+ message += mAuthority;
+ } else {
+ message += Arrays.toString(mAuthorities);
+ }
+ throw new SecurityException(message);
+ }
+ }
+
/** @hide */
@VisibleForTesting
public Uri validateIncomingUri(Uri uri) throws SecurityException {
@@ -2138,16 +2184,7 @@
+ mContext.getUserId() + " with a uri belonging to user " + userId);
}
}
- if (!matchesOurAuthorities(getAuthorityWithoutUserId(auth))) {
- String message = "The authority of the uri " + uri + " does not match the one of the "
- + "contentProvider: ";
- if (mAuthority != null) {
- message += mAuthority;
- } else {
- message += Arrays.toString(mAuthorities);
- }
- throw new SecurityException(message);
- }
+ validateIncomingAuthority(auth);
// Normalize the path by removing any empty path segments, which can be
// a source of security issues.
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index d315f49..0b5bdb5 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -16,8 +16,12 @@
package android.content;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
import android.database.CrossProcessCursorWrapper;
@@ -65,7 +69,7 @@
* on the ContentProviderClient those calls are made from until you are finished
* with the data they have returned.
*/
-public class ContentProviderClient implements AutoCloseable {
+public class ContentProviderClient implements ContentInterface, AutoCloseable {
private static final String TAG = "ContentProviderClient";
@GuardedBy("ContentProviderClient.class")
@@ -76,6 +80,7 @@
private final IContentProvider mContentProvider;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mPackageName;
+ private final String mAuthority;
private final boolean mStable;
private final AtomicBoolean mClosed = new AtomicBoolean();
@@ -86,19 +91,39 @@
/** {@hide} */
@VisibleForTesting
- public ContentProviderClient(
- ContentResolver contentResolver, IContentProvider contentProvider, boolean stable) {
+ public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
+ boolean stable) {
+ // Only used for testing, so use a fake authority
+ this(contentResolver, contentProvider, "unknown", stable);
+ }
+
+ /** {@hide} */
+ public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
+ String authority, boolean stable) {
mContentResolver = contentResolver;
mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
+ mAuthority = authority;
mStable = stable;
mCloseGuard.open("close");
}
- /** {@hide} */
- public void setDetectNotResponding(long timeoutMillis) {
+ /**
+ * Configure this client to automatically detect and kill the remote
+ * provider when an "application not responding" event is detected.
+ *
+ * @param timeoutMillis the duration for which a pending call is allowed
+ * block before the remote provider is considered to be
+ * unresponsive. Set to {@code 0} to allow pending calls to block
+ * indefinitely with no action taken.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
+ public void setDetectNotResponding(@DurationMillisLong long timeoutMillis) {
synchronized (ContentProviderClient.class) {
mAnrTimeout = timeoutMillis;
@@ -153,6 +178,7 @@
}
/** See {@link ContentProvider#query ContentProvider.query} */
+ @Override
public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
throws RemoteException {
@@ -183,6 +209,7 @@
}
/** See {@link ContentProvider#getType ContentProvider.getType} */
+ @Override
public @Nullable String getType(@NonNull Uri url) throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -200,6 +227,7 @@
}
/** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
+ @Override
public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter)
throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -219,6 +247,7 @@
}
/** See {@link ContentProvider#canonicalize} */
+ @Override
public final @Nullable Uri canonicalize(@NonNull Uri url) throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -236,6 +265,7 @@
}
/** See {@link ContentProvider#uncanonicalize} */
+ @Override
public final @Nullable Uri uncanonicalize(@NonNull Uri url) throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -253,6 +283,7 @@
}
/** See {@link ContentProvider#refresh} */
+ @Override
public boolean refresh(Uri url, @Nullable Bundle args,
@Nullable CancellationSignal cancellationSignal) throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -277,6 +308,7 @@
}
/** See {@link ContentProvider#insert ContentProvider.insert} */
+ @Override
public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -295,6 +327,7 @@
}
/** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
+ @Override
public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] initialValues)
throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -314,6 +347,7 @@
}
/** See {@link ContentProvider#delete ContentProvider.delete} */
+ @Override
public int delete(@NonNull Uri url, @Nullable String selection,
@Nullable String[] selectionArgs) throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -332,6 +366,7 @@
}
/** See {@link ContentProvider#update ContentProvider.update} */
+ @Override
public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -368,6 +403,7 @@
* you use the {@link ContentResolver#openFileDescriptor
* ContentResolver.openFileDescriptor} API instead.
*/
+ @Override
public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode,
@Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
Preconditions.checkNotNull(url, "url");
@@ -411,6 +447,7 @@
* you use the {@link ContentResolver#openAssetFileDescriptor
* ContentResolver.openAssetFileDescriptor} API instead.
*/
+ @Override
public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode,
@Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
Preconditions.checkNotNull(url, "url");
@@ -446,8 +483,15 @@
public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
@NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)
throws RemoteException, FileNotFoundException {
+ return openTypedAssetFile(uri, mimeType, opts, signal);
+ }
+
+ @Override
+ public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+ @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+ @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
Preconditions.checkNotNull(uri, "uri");
- Preconditions.checkNotNull(mimeType, "mimeType");
+ Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
beforeRemote();
try {
@@ -458,7 +502,7 @@
signal.setRemote(remoteSignal);
}
return mContentProvider.openTypedAssetFile(
- mPackageName, uri, mimeType, opts, remoteSignal);
+ mPackageName, uri, mimeTypeFilter, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -472,12 +516,20 @@
/** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
public @NonNull ContentProviderResult[] applyBatch(
@NonNull ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException {
+ throws RemoteException, OperationApplicationException {
+ return applyBatch(mAuthority, operations);
+ }
+
+ /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
+ @Override
+ public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+ @NonNull ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
Preconditions.checkNotNull(operations, "operations");
beforeRemote();
try {
- return mContentProvider.applyBatch(mPackageName, operations);
+ return mContentProvider.applyBatch(mPackageName, authority, operations);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -491,11 +543,19 @@
/** See {@link ContentProvider#call(String, String, Bundle)} */
public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
@Nullable Bundle extras) throws RemoteException {
+ return call(mAuthority, method, arg, extras);
+ }
+
+ /** See {@link ContentProvider#call(String, String, Bundle)} */
+ @Override
+ public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+ @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
+ Preconditions.checkNotNull(authority, "authority");
Preconditions.checkNotNull(method, "method");
beforeRemote();
try {
- return mContentProvider.call(mPackageName, method, arg, extras);
+ return mContentProvider.call(mPackageName, authority, method, arg, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 6bede13..ca657b1 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -174,13 +174,15 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String authority = data.readString();
final int numOperations = data.readInt();
final ArrayList<ContentProviderOperation> operations =
new ArrayList<>(numOperations);
for (int i = 0; i < numOperations; i++) {
operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
}
- final ContentProviderResult[] results = applyBatch(callingPkg, operations);
+ final ContentProviderResult[] results = applyBatch(callingPkg, authority,
+ operations);
reply.writeNoException();
reply.writeTypedArray(results, 0);
return true;
@@ -267,11 +269,12 @@
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String authority = data.readString();
String method = data.readString();
String stringArg = data.readString();
Bundle args = data.readBundle();
- Bundle responseBundle = call(callingPkg, method, stringArg, args);
+ Bundle responseBundle = call(callingPkg, authority, method, stringArg, args);
reply.writeNoException();
reply.writeBundle(responseBundle);
@@ -507,7 +510,7 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg,
+ public ContentProviderResult[] applyBatch(String callingPkg, String authority,
ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
Parcel data = Parcel.obtain();
@@ -515,6 +518,7 @@
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(authority);
data.writeInt(operations.size());
for (ContentProviderOperation operation : operations) {
operation.writeToParcel(data, 0);
@@ -636,14 +640,15 @@
}
@Override
- public Bundle call(String callingPkg, String method, String request, Bundle args)
- throws RemoteException {
+ public Bundle call(String callingPkg, String authority, String method, String request,
+ Bundle args) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(authority);
data.writeString(method);
data.writeString(request);
data.writeBundle(args);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7d5202d..6704a45 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
@@ -52,7 +53,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.text.TextUtils;
@@ -88,7 +88,7 @@
* <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
* developer guide.</p>
*/
-public abstract class ContentResolver {
+public abstract class ContentResolver implements ContentInterface {
/**
* Enables logic that supports deprecation of {@code _data} columns,
* typically by replacing values with fake paths that the OS then offers to
@@ -655,6 +655,7 @@
* using the content:// scheme.
* @return A MIME type for the content, or null if the URL is invalid or the type is unknown
*/
+ @Override
public final @Nullable String getType(@NonNull Uri url) {
Preconditions.checkNotNull(url, "url");
@@ -708,6 +709,7 @@
* data streams that match the given mimeTypeFilter. If there are none,
* null is returned.
*/
+ @Override
public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter) {
Preconditions.checkNotNull(url, "url");
Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
@@ -835,6 +837,7 @@
* @return A Cursor object, which is positioned before the first entry, or null
* @see Cursor
*/
+ @Override
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
@@ -935,6 +938,7 @@
*
* @see #uncanonicalize
*/
+ @Override
public final @Nullable Uri canonicalize(@NonNull Uri url) {
Preconditions.checkNotNull(url, "url");
IContentProvider provider = acquireProvider(url);
@@ -971,6 +975,7 @@
*
* @see #canonicalize
*/
+ @Override
public final @Nullable Uri uncanonicalize(@NonNull Uri url) {
Preconditions.checkNotNull(url, "url");
IContentProvider provider = acquireProvider(url);
@@ -1005,6 +1010,7 @@
* canceled the refresh request.
* @return true if the provider actually tried refreshing.
*/
+ @Override
public final boolean refresh(@NonNull Uri url, @Nullable Bundle args,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(url, "url");
@@ -1116,6 +1122,12 @@
}
}
+ @Override
+ public final @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+ @Nullable CancellationSignal signal) throws FileNotFoundException {
+ return openFileDescriptor(uri, mode, signal);
+ }
+
/**
* Open a raw file descriptor to access data under a URI. This
* is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
@@ -1221,6 +1233,12 @@
throw new FileNotFoundException("Not a whole file");
}
+ @Override
+ public final @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+ @Nullable CancellationSignal signal) throws FileNotFoundException {
+ return openAssetFileDescriptor(uri, mode, signal);
+ }
+
/**
* Open a raw file descriptor to access data under a URI. This
* interacts with the underlying {@link ContentProvider#openAssetFile}
@@ -1425,6 +1443,13 @@
}
}
+ @Override
+ public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+ @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+ @Nullable CancellationSignal signal) throws FileNotFoundException {
+ return openTypedAssetFileDescriptor(uri, mimeTypeFilter, opts, signal);
+ }
+
/**
* Open a raw file descriptor to access (potentially type transformed)
* data from a "content:" URI. This interacts with the underlying
@@ -1634,6 +1659,7 @@
* the field. Passing an empty ContentValues will create an empty row.
* @return the URL of the newly created row.
*/
+ @Override
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@Nullable ContentValues values) {
Preconditions.checkNotNull(url, "url");
@@ -1672,6 +1698,7 @@
* @throws RemoteException thrown if a RemoteException is encountered while attempting
* to communicate with a remote provider.
*/
+ @Override
public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
@NonNull ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
@@ -1698,6 +1725,7 @@
* the field. Passing null will create an empty row.
* @return the number of newly created rows.
*/
+ @Override
public final int bulkInsert(@RequiresPermission.Write @NonNull Uri url,
@NonNull ContentValues[] values) {
Preconditions.checkNotNull(url, "url");
@@ -1731,6 +1759,7 @@
(excluding the WHERE itself).
* @return The number of rows deleted.
*/
+ @Override
public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
@Nullable String[] selectionArgs) {
Preconditions.checkNotNull(url, "url");
@@ -1766,6 +1795,7 @@
* @return the number of rows updated.
* @throws NullPointerException if uri or values are null
*/
+ @Override
public final int update(@RequiresPermission.Write @NonNull Uri uri,
@Nullable ContentValues values, @Nullable String where,
@Nullable String[] selectionArgs) {
@@ -1805,14 +1835,20 @@
*/
public final @Nullable Bundle call(@NonNull Uri uri, @NonNull String method,
@Nullable String arg, @Nullable Bundle extras) {
- Preconditions.checkNotNull(uri, "uri");
+ return call(uri.getAuthority(), method, arg, extras);
+ }
+
+ @Override
+ public final @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+ @Nullable String arg, @Nullable Bundle extras) {
+ Preconditions.checkNotNull(authority, "authority");
Preconditions.checkNotNull(method, "method");
- IContentProvider provider = acquireProvider(uri);
+ IContentProvider provider = acquireProvider(authority);
if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
+ throw new IllegalArgumentException("Unknown authority " + authority);
}
try {
- final Bundle res = provider.call(mPackageName, method, arg, extras);
+ final Bundle res = provider.call(mPackageName, authority, method, arg, extras);
Bundle.setDefusable(res, true);
return res;
} catch (RemoteException e) {
@@ -1918,7 +1954,7 @@
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = acquireProvider(uri);
if (provider != null) {
- return new ContentProviderClient(this, provider, true);
+ return new ContentProviderClient(this, provider, uri.getAuthority(), true);
}
return null;
}
@@ -1939,7 +1975,7 @@
Preconditions.checkNotNull(name, "name");
IContentProvider provider = acquireProvider(name);
if (provider != null) {
- return new ContentProviderClient(this, provider, true);
+ return new ContentProviderClient(this, provider, name, true);
}
return null;
@@ -1966,7 +2002,7 @@
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = acquireUnstableProvider(uri);
if (provider != null) {
- return new ContentProviderClient(this, provider, false);
+ return new ContentProviderClient(this, provider, uri.getAuthority(), false);
}
return null;
@@ -1993,7 +2029,7 @@
Preconditions.checkNotNull(name, "name");
IContentProvider provider = acquireUnstableProvider(name);
if (provider != null) {
- return new ContentProviderClient(this, provider, false);
+ return new ContentProviderClient(this, provider, name, false);
}
return null;
@@ -2943,7 +2979,12 @@
}
}
- /** {@hide} */
+ /**
+ * Put the cache with the key.
+ *
+ * @param key the key to add
+ * @param value the value to add
+ */
public void putCache(Uri key, Bundle value) {
try {
getContentService().putCache(mContext.getPackageName(), key, value,
@@ -2953,7 +2994,13 @@
}
}
- /** {@hide} */
+ /**
+ * Get the cache with the key.
+ *
+ * @param key the key to get the value
+ * @return the matched value. If the key doesn't exist, will return null.
+ * @see #putCache(Uri, Bundle)
+ */
public Bundle getCache(Uri key) {
try {
final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
@@ -3141,7 +3188,14 @@
return mContext.getUserId();
}
- /** @hide */
+ /**
+ * Get the system drawable of the mime type.
+ *
+ * @param mimeType the requested mime type
+ * @return the matched drawable
+ * @hide
+ */
+ @SystemApi
public Drawable getTypeDrawable(String mimeType) {
return MimeIconUtils.loadMimeIcon(mContext, mimeType);
}
@@ -3248,10 +3302,10 @@
}
/** {@hide} */
- public static Bitmap loadThumbnail(@NonNull ContentProviderClient client, @NonNull Uri uri,
+ public static Bitmap loadThumbnail(@NonNull ContentInterface content, @NonNull Uri uri,
@NonNull Size size, @Nullable CancellationSignal signal, int allocator)
throws IOException {
- Objects.requireNonNull(client);
+ Objects.requireNonNull(content);
Objects.requireNonNull(uri);
Objects.requireNonNull(size);
@@ -3260,7 +3314,7 @@
opts.putParcelable(EXTRA_SIZE, Point.convert(size));
return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
- return client.openTypedAssetFileDescriptor(uri, "image/*", opts, signal);
+ return content.openTypedAssetFile(uri, "image/*", opts, signal);
}), (ImageDecoder decoder, ImageInfo info, Source source) -> {
decoder.setAllocator(allocator);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 68aac64..d7d3cb5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3003,6 +3003,11 @@
* how the process will be managed in some cases based on those flags. Currently only
* works on isolated processes (will be ignored for non-isolated processes).
*
+ * <p>Note that this call does not take immediate effect, but will be applied the next
+ * time the impacted process is adjusted for some other reason. Typically you would
+ * call this before then calling a new {@link #bindIsolatedService} on the service
+ * of interest, with that binding causing the process to be shuffled accordingly.</p>
+ *
* @param conn The connection interface previously supplied to bindService(). This
* parameter must not be null.
* @param group A group to put this connection's process in. Upon calling here, this
@@ -3126,6 +3131,7 @@
//@hide: HDMI_CONTROL_SERVICE,
INPUT_SERVICE,
DISPLAY_SERVICE,
+ //@hide COLOR_DISPLAY_SERVICE,
USER_SERVICE,
RESTRICTIONS_SERVICE,
APP_OPS_SERVICE,
@@ -3933,13 +3939,12 @@
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
- * Official published name of the smart suggestions service.
+ * Official published name of the content capture service.
*
* @hide
* @see #getSystemService(String)
*/
- // TODO(b/111276913): rename string (will require SELinux change first)
- public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "intelligence";
+ public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
/**
* Use with {@link #getSystemService(String)} to access the
@@ -4108,6 +4113,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.hardware.display.ColorDisplayManager} for controlling color transforms.
+ *
+ * @see #getSystemService(String)
+ * @see android.hardware.display.ColorDisplayManager
+ * @hide
+ */
+ public static final String COLOR_DISPLAY_SERVICE = "color_display";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.UserManager} for managing users on devices that support multiple users.
*
* @see #getSystemService(String)
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 044ed61..0427c2f 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -60,13 +60,28 @@
public AssetFileDescriptor openAssetFile(
String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
- public ContentProviderResult[] applyBatch(String callingPkg,
+
+ @Deprecated
+ public default ContentProviderResult[] applyBatch(String callingPkg,
ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException;
+ throws RemoteException, OperationApplicationException {
+ return applyBatch(callingPkg, "unknown", operations);
+ }
+
+ public ContentProviderResult[] applyBatch(String callingPkg, String authority,
+ ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException;
+
+ @Deprecated
@UnsupportedAppUsage
- public Bundle call(
- String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras)
- throws RemoteException;
+ public default Bundle call(String callingPkg, String method,
+ @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
+ return call(callingPkg, "unknown", method, arg, extras);
+ }
+
+ public Bundle call(String callingPkg, String authority, String method,
+ @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+
public ICancellationSignal createCancellationSignal() throws RemoteException;
public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index edfb3a7..2f0618c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1916,12 +1916,15 @@
/**
* Activity action: Launch UI to review app uses of permissions.
* <p>
- * Input: Nothing
+ * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission name
+ * that will be displayed by the launched UI.
* </p>
* <p>
* Output: Nothing.
* </p>
*
+ * @see #EXTRA_PERMISSION_NAME
+ *
* @hide
*/
@SystemApi
@@ -5239,6 +5242,7 @@
/**
* Optional CharSequence extra to provide a search query.
+ * The format of this query is dependent on the receiving application.
*
* <p>Applicable to {@link Intent} with actions:
* <ul>
@@ -5263,8 +5267,6 @@
* Used as a boolean extra field in {@link #ACTION_CHOOSER} intents to specify
* whether to show the chooser or not when there is only one application available
* to choose from.
- *
- * @hide
*/
public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE =
"android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 98a135f..c361ac1 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -631,6 +631,13 @@
*/
public static final int PRIVATE_FLAG_PROFILEABLE_BY_SHELL = 1 << 23;
+ /**
+ * Indicates whether this package requires access to non-SDK APIs.
+ * Only system apps and tests are allowed to use this property.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_HAS_FRAGILE_USER_DATA = 1 << 24;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -655,6 +662,7 @@
PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
PRIVATE_FLAG_VENDOR,
PRIVATE_FLAG_VIRTUAL_PRELOAD,
+ PRIVATE_FLAG_HAS_FRAGILE_USER_DATA,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlags {}
@@ -1303,7 +1311,7 @@
/** {@hide} */
public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) {
long token = proto.start(fieldId);
- super.writeToProto(proto, ApplicationInfoProto.PACKAGE);
+ super.writeToProto(proto, ApplicationInfoProto.PACKAGE, dumpFlags);
proto.write(ApplicationInfoProto.PERMISSION, permission);
proto.write(ApplicationInfoProto.PROCESS_NAME, processName);
proto.write(ApplicationInfoProto.UID, uid);
@@ -1730,6 +1738,17 @@
return (privateFlags & PRIVATE_FLAG_USES_NON_SDK_API) != 0;
}
+ /**
+ * Whether an app needs to keep the app data on uninstall.
+ *
+ * @return {@code true} if the app indicates that it needs to keep the app data
+ *
+ * @hide
+ */
+ public boolean hasFragileUserData() {
+ return (privateFlags & PRIVATE_FLAG_HAS_FRAGILE_USER_DATA) != 0;
+ }
+
private boolean isAllowedToUseHiddenApis() {
if (isSignedWithPlatformKey()) {
return true;
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 1c564f3..740fd7f 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -16,6 +16,8 @@
package android.content.pm;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -66,7 +68,30 @@
mContext.getIApplicationThread(),
mContext.getPackageName(),
component,
- targetUser);
+ targetUser.getIdentifier(),
+ true);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts the specified activity of the caller package in the specified profile if the caller
+ * has {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} permission and
+ * both the caller and target user profiles are in the same user group.
+ *
+ * @param component The ComponentName of the activity to launch. It must be exported.
+ * @param targetUser The UserHandle of the profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
+ public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+ try {
+ mService.startActivityAsUser(mContext.getIApplicationThread(),
+ mContext.getPackageName(), component, targetUser.getIdentifier(), false);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index bc2f92a..d2d66cb 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -28,6 +28,6 @@
*/
interface ICrossProfileApps {
void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
- in ComponentName component, in UserHandle user);
+ in ComponentName component, int userId, boolean launchMainActivity);
List<UserHandle> getTargetUserProfiles(in String callingPackage);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index ecc8cd6..a251c00 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -42,6 +42,8 @@
ParceledListSlice getAllSessions(int userId);
ParceledListSlice getMySessions(String installerPackageName, int userId);
+ ParceledListSlice getStagedSessions();
+
void registerCallback(IPackageInstallerCallback callback, int userId);
void unregisterCallback(IPackageInstallerCallback callback);
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index cef21f6..04e15c7 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -38,9 +38,12 @@
void commit(in IntentSender statusReceiver, boolean forTransferred);
void transfer(in String packageName);
void abandon();
+
boolean isMultiPackage();
int[] getChildSessionIds();
void addChildSessionId(in int sessionId);
void removeChildSessionId(in int sessionId);
int getParentSessionId();
+
+ boolean isStaged();
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index dbea821..eea2b88 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -675,6 +675,8 @@
String getSystemTextClassifierPackageName();
+ String getWellbeingPackageName();
+
boolean isPackageStateProtected(String packageName, int userId);
void sendDeviceCustomizationReadyBroadcast();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 07672d9..f81eb76 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -68,7 +68,14 @@
* {@link PackageInstaller.Session}, which any app can create. Once the session
* is created, the installer can stream one or more APKs into place until it
* decides to either commit or destroy the session. Committing may require user
- * intervention to complete the installation.
+ * intervention to complete the installation, unless the caller falls into one of the
+ * following categories, in which case the installation will complete automatically.
+ * <ul>
+ * <li>the device owner
+ * <li>the affiliated profile owner
+ * <li>the device owner delegated app with
+ * {@link android.app.admin.DevicePolicyManager#DELEGATION_PACKAGE_INSTALLATION}
+ * </ul>
* <p>
* Sessions can install brand new apps, upgrade existing apps, or add new splits
* into an existing app.
@@ -463,12 +470,26 @@
}
/**
+ * Return list of all staged install sessions.
+ */
+ public @NonNull List<SessionInfo> getStagedSessions() {
+ try {
+ // TODO: limit this to the mUserId?
+ return mInstaller.getStagedSessions().getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Uninstall the given package, removing it completely from the device. This
* method is available to:
* <ul>
* <li>the current "installer of record" for the package
* <li>the device owner
* <li>the affiliated profile owner
+ * <li>the device owner delegated app with
+ * {@link android.app.admin.DevicePolicyManager#DELEGATION_PACKAGE_INSTALLATION}
* </ul>
*
* @param packageName The package to uninstall.
@@ -779,6 +800,11 @@
* individual session IDs can be added with {@link #addChildSessionId(int)}
* and commit of the multi-package session will result in all child sessions
* being committed atomically.
+ * <p>
+ * If a package requires to be installed only at reboot, the session should
+ * be marked as a staged session by calling {@link SessionParams#setStaged()}
+ * with {@code true}. This can also apply to a multi-package session, in
+ * which case all the packages in the session will be applied at reboot.
*/
public static class Session implements Closeable {
/** {@hide} */
@@ -1105,6 +1131,17 @@
}
/**
+ * @return {@code true} if this session will be staged and applied at next reboot.
+ */
+ public boolean isStaged() {
+ try {
+ return mSession.isStaged();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return the session ID of the multi-package session that this belongs to or
* {@link SessionInfo#INVALID_ID} if it does not belong to a multi-package session.
*/
@@ -1227,6 +1264,8 @@
public String installerPackageName;
/** {@hide} */
public boolean isMultiPackage;
+ /** {@hide} */
+ public boolean isStaged;
/**
* Construct parameters for a new package install session.
@@ -1257,6 +1296,7 @@
grantedRuntimePermissions = source.readStringArray();
installerPackageName = source.readString();
isMultiPackage = source.readBoolean();
+ isStaged = source.readBoolean();
}
/**
@@ -1471,6 +1511,17 @@
this.isMultiPackage = true;
}
+ /**
+ * Set this session to be staged to be installed at reboot.
+ *
+ * Staged sessions are scheduled to be installed at next reboot. Staged sessions can also be
+ * 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.
+ */
+ public void setStaged() {
+ this.isStaged = true;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1488,6 +1539,7 @@
pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("isMultiPackage", isMultiPackage);
+ pw.printPair("isStaged", isStaged);
pw.println();
}
@@ -1514,6 +1566,7 @@
dest.writeStringArray(grantedRuntimePermissions);
dest.writeString(installerPackageName);
dest.writeBoolean(isMultiPackage);
+ dest.writeBoolean(isStaged);
}
public static final Parcelable.Creator<SessionParams>
@@ -1593,6 +1646,8 @@
/** {@hide} */
public boolean isMultiPackage;
/** {@hide} */
+ public boolean isStaged;
+ /** {@hide} */
public int parentSessionId = INVALID_ID;
/** {@hide} */
public int[] childSessionIds = NO_SESSIONS;
@@ -1625,6 +1680,7 @@
grantedRuntimePermissions = source.readStringArray();
installFlags = source.readInt();
isMultiPackage = source.readBoolean();
+ isStaged = source.readBoolean();
parentSessionId = source.readInt();
childSessionIds = source.createIntArray();
if (childSessionIds == null) {
@@ -1892,6 +1948,13 @@
}
/**
+ * Returns true if this session is a staged session which will be applied at next reboot.
+ */
+ public boolean isStaged() {
+ return isStaged;
+ }
+
+ /**
* Returns the parent multi-package session ID if this session belongs to one,
* {@link #INVALID_ID} otherwise.
*/
@@ -1935,6 +1998,7 @@
dest.writeStringArray(grantedRuntimePermissions);
dest.writeInt(installFlags);
dest.writeBoolean(isMultiPackage);
+ dest.writeBoolean(isStaged);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
}
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index cdb7814..ff7b347 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -433,18 +433,18 @@
/**
* @hide
*/
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) {
long token = proto.start(fieldId);
if (name != null) {
proto.write(PackageItemInfoProto.NAME, name);
}
proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName);
- if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) {
- proto.write(PackageItemInfoProto.LABEL_RES, labelRes);
+ proto.write(PackageItemInfoProto.LABEL_RES, labelRes);
+ if (nonLocalizedLabel != null) {
proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString());
- proto.write(PackageItemInfoProto.ICON, icon);
- proto.write(PackageItemInfoProto.BANNER, banner);
}
+ proto.write(PackageItemInfoProto.ICON, icon);
+ proto.write(PackageItemInfoProto.BANNER, banner);
proto.end(token);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b7df2bf..34cdfee 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1399,6 +1399,16 @@
public static final int DELETE_DONT_KILL_APP = 0x00000008;
/**
+ * Flag parameter for {@link #deletePackage} to indicate that any
+ * contributed media should also be deleted during this uninstall. The
+ * meaning of "contributed" means it won't automatically be deleted when the
+ * app is uninstalled.
+ *
+ * @hide
+ */
+ public static final int DELETE_CONTRIBUTED_MEDIA = 0x00000010;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that package deletion
* should be chatty.
*
@@ -2687,6 +2697,16 @@
"android.software.device_id_attestation";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * the requisite kernel support for multinetworking-capable IPsec tunnels.
+ *
+ * <p>This feature implies that the device supports XFRM Interfaces (CONFIG_XFRM_INTERFACE), or
+ * VTIs with kernel patches allowing updates of output/set mark via UPDSA.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
+
+ /**
* Extra field name for the URI to a verification file. Passed to a package
* verifier.
*
@@ -3357,6 +3377,33 @@
@ApplicationInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
/**
+ * Retrieve all of the information we know about a particular
+ * package/application, for a specific user.
+ *
+ * @param packageName The full name (i.e. com.google.apps.contacts) of an
+ * application.
+ * @param flags Additional option flags to modify the data returned.
+ * @return An {@link ApplicationInfo} containing information about the
+ * package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if
+ * the package is not found in the list of installed applications,
+ * the application information is retrieved from the list of
+ * uninstalled applications (which includes installed applications
+ * as well as applications with data directory i.e. applications
+ * which had been deleted with {@code DONT_DELETE_DATA} flag set).
+ * @throws NameNotFoundException if a package with the given name cannot be
+ * found on the system.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+ @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+ throws NameNotFoundException {
+ return getApplicationInfoAsUser(packageName, flags, user.getIdentifier());
+ }
+
+ /**
* Retrieve all of the information we know about a particular activity
* class.
*
@@ -4183,6 +4230,32 @@
@ResolveInfoFlags int flags, @UserIdInt int userId);
/**
+ * Retrieve all activities that can be performed for the given intent, for a
+ * specific user.
+ *
+ * @param intent The desired intent as per resolveActivity().
+ * @param flags Additional option flags to modify the data returned. The
+ * most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+ * resolution to only those activities that support the
+ * {@link android.content.Intent#CATEGORY_DEFAULT}. Or, set
+ * {@link #MATCH_ALL} to prevent any filtering of the results.
+ * @param user The user being queried.
+ * @return Returns a List of ResolveInfo objects containing one entry for
+ * each matching activity, ordered from best to worst. In other
+ * words, the first item is what would be returned by
+ * {@link #resolveActivity}. If there are no matching activities, an
+ * empty list is returned.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+ @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ return queryIntentActivitiesAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
* Retrieve a set of activities that should be presented to the user as
* similar options. This is like {@link #queryIntentActivities}, except it
* also allows you to supply a list of more explicit Intents that you would
@@ -4314,6 +4387,27 @@
@ResolveInfoFlags int flags, @UserIdInt int userId);
/**
+ * Retrieve all services that can match the given intent for a given user.
+ *
+ * @param intent The desired intent as per resolveService().
+ * @param flags Additional option flags to modify the data returned.
+ * @param user The user being queried.
+ * @return Returns a List of ResolveInfo objects containing one entry for
+ * each matching service, ordered from best to worst. In other
+ * words, the first item is what would be returned by
+ * {@link #resolveService}. If there are no matching services, an
+ * empty list or null is returned.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+ @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ return queryIntentServicesAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
* Retrieve all providers that can match the given intent.
*
* @param intent An intent containing all of the desired specification
@@ -4335,6 +4429,26 @@
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned.
+ * @param user The user being queried.
+ * @return Returns a List of ResolveInfo objects containing one entry for
+ * each matching provider, ordered from best to worst. If there are
+ * no matching services, an empty list or null is returned.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentContentProvidersAsUser(@NonNull Intent intent,
+ @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ return queryIntentContentProvidersAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
+ * Retrieve all providers that can match the given intent.
+ *
+ * @param intent An intent containing all of the desired specification
+ * (action, data, type, category, and/or component).
+ * @param flags Additional option flags to modify the data returned.
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching provider, ordered from best to worst. If there are
* no matching services, an empty list or null is returned.
@@ -6424,6 +6538,17 @@
}
/**
+ * @return the wellbeing app package name, or null if it's not defined by the OEM.
+ *
+ * @hide
+ */
+ @TestApi
+ public String getWellbeingPackageName() {
+ throw new UnsupportedOperationException(
+ "getWellbeingPackageName not implemented in subclass");
+ }
+
+ /**
* @return whether a given package's state is protected, e.g. package cannot be disabled,
* suspended, hidden or force stopped.
*
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index b49c447..5db9f50 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -53,6 +53,8 @@
public static final int PACKAGE_BROWSER = 4;
public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
+ public static final int PACKAGE_WELLBEING = 7;
+ public static final int PACKAGE_DOCUMENTER = 8;
@IntDef(value = {
PACKAGE_SYSTEM,
PACKAGE_SETUP_WIZARD,
@@ -61,6 +63,8 @@
PACKAGE_BROWSER,
PACKAGE_SYSTEM_TEXT_CLASSIFIER,
PACKAGE_PERMISSION_CONTROLLER,
+ PACKAGE_WELLBEING,
+ PACKAGE_DOCUMENTER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KnownPackage {}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ac18dca..61a74de 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3710,6 +3710,12 @@
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_hasFragileUserData,
+ false)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA;
+ }
+
if (outError[0] == null) {
CharSequence pname;
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
@@ -5380,6 +5386,10 @@
s.info.splitName =
sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0);
+ s.info.mForegroundServiceType = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestService_foregroundServiceType,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED);
+
s.info.flags = 0;
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index d9d6b5f..bb8c92d 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -181,6 +181,28 @@
@TestApi
public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
+ /**
+ * Additional flag for {${link #protectionLevel}, corresponding
+ * to the <code>wellbeing</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_WELLBEING = 0x20000;
+
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding to the
+ * {@code documenter} value of {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000;
+
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -197,6 +219,8 @@
PROTECTION_FLAG_OEM,
PROTECTION_FLAG_VENDOR_PRIVILEGED,
PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+ PROTECTION_FLAG_WELLBEING,
+ PROTECTION_FLAG_DOCUMENTER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -386,6 +410,12 @@
if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
protLevel += "|textClassifier";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
+ protLevel += "|wellbeing";
+ }
+ if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
+ protLevel += "|documenter";
+ }
return protLevel;
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index ad2c72274..8300c0c 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -16,10 +16,14 @@
package android.content.pm;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Printer;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Information you can retrieve about a particular application
* service. This corresponds to information collected from the
@@ -94,6 +98,81 @@
*/
public int flags;
+ /**
+ * The default foreground service type if not been set in manifest file.
+ */
+ public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0;
+
+ /**
+ * Constant corresponding to <code>sync</code> in
+ * the {@link android.R.attr#foregroundServiceType} attribute.
+ * Data(photo, file, account) upload/download, backup/restore, import/export, fetch,
+ * transfer over network between device and cloud.
+ */
+ public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1;
+
+ /**
+ * Constant corresponding to <code>mediaPlay</code> in
+ * the {@link android.R.attr#foregroundServiceType} attribute.
+ * Music, video, news or other media play.
+ */
+ public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2;
+
+ /**
+ * Constant corresponding to <code>phoneCall</code> in
+ * the {@link android.R.attr#foregroundServiceType} attribute.
+ * Ongoing phone call or video conference.
+ */
+ public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3;
+
+ /**
+ * Constant corresponding to <code>location</code> in
+ * the {@link android.R.attr#foregroundServiceType} attribute.
+ * GPS, map, navigation location update.
+ */
+ public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4;
+
+ /**
+ * Constant corresponding to <code>deviceCompanion</code> in
+ * the {@link android.R.attr#foregroundServiceType} attribute.
+ * Auto, bluetooth, TV or other devices connection, monitoring and interaction.
+ */
+ public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5;
+
+ /**
+ * Constant corresponding to <code>ongoingProcess</code> in
+ * the {@link android.R.attr#foregroundServiceType} attribute.
+ * Process that should not be interrupted, including installation, setup, photo
+ * compression etc.
+ */
+ public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6;
+
+ /**
+ * The enumeration of values for foreground service type.
+ * The foreground service type is set in {@link android.R.attr#foregroundServiceType}
+ * attribute.
+ * @hide
+ */
+ @IntDef(flag = false, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
+ FOREGROUND_SERVICE_TYPE_UNSPECIFIED,
+ FOREGROUND_SERVICE_TYPE_SYNC,
+ FOREGROUND_SERVICE_TYPE_MEDIA_PLAY,
+ FOREGROUND_SERVICE_TYPE_PHONE_CALL,
+ FOREGROUND_SERVICE_TYPE_LOCATION,
+ FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION,
+ FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ForegroundServiceType {}
+
+ /**
+ * The type of foreground service, set in
+ * {@link android.R.attr#foregroundServiceType} attribute, one value in
+ * {@link ForegroundServiceType}
+ * @hide
+ */
+ public @ForegroundServiceType int mForegroundServiceType = FOREGROUND_SERVICE_TYPE_UNSPECIFIED;
+
public ServiceInfo() {
}
@@ -101,6 +180,15 @@
super(orig);
permission = orig.permission;
flags = orig.flags;
+ mForegroundServiceType = orig.mForegroundServiceType;
+ }
+
+ /**
+ * Return the current foreground service type.
+ * @return the current foreground service type.
+ */
+ public int getForegroundServiceType() {
+ return mForegroundServiceType;
}
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 5afc8a9..a607a9f 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,15 +22,15 @@
*/
public class SharedLibraryNames {
- public static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
+ static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
- public static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
+ static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
- public static final String ANDROID_TEST_BASE = "android.test.base";
+ static final String ANDROID_TEST_BASE = "android.test.base";
- public static final String ANDROID_TEST_MOCK = "android.test.mock";
+ static final String ANDROID_TEST_MOCK = "android.test.mock";
- public static final String ANDROID_TEST_RUNNER = "android.test.runner";
+ static final String ANDROID_TEST_RUNNER = "android.test.runner";
public static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
}
diff --git a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
index 9490e27..2faf3ad 100644
--- a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
+++ b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
@@ -22,6 +22,8 @@
* Interface for communication with the permission presenter service.
*
* @hide
+ * @deprecated Only available to keep
+ * android.permissionpresenterservice.RuntimePermissionPresenterService functional
*/
oneway interface IRuntimePermissionPresenter {
void getAppPermissions(String packageName, in RemoteCallback callback);
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
index 352e8ad..9fa863c 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
@@ -29,7 +29,11 @@
* coarse and fine platform permissions.
*
* @hide
+ *
+ * @deprecated Not used anymore. Use {@link android.permission.RuntimePermissionPresentationInfo}
+ * instead
*/
+@Deprecated
@SystemApi
public final class RuntimePermissionPresentationInfo implements Parcelable {
private static final int FLAG_GRANTED = 1 << 0;
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
deleted file mode 100644
index 79bc9a3..0000000
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.permission;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-import android.permissionpresenterservice.RuntimePermissionPresenterService;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.SomeArgs;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This class provides information about runtime permissions for a specific
- * app or all apps. This information is dedicated for presentation purposes
- * and does not necessarily reflect the individual permissions requested/
- * granted to an app as the platform may be grouping permissions to improve
- * presentation and help the user make an informed choice. For example, all
- * runtime permissions in the same permission group may be presented as a
- * single permission in the UI.
- *
- * @hide
- */
-public final class RuntimePermissionPresenter {
- private static final String TAG = "RuntimePermPresenter";
-
- /**
- * The key for retrieving the result from the returned bundle.
- *
- * @hide
- */
- public static final String KEY_RESULT =
- "android.content.pm.permission.RuntimePermissionPresenter.key.result";
-
- /**
- * Listener for delivering a result.
- */
- public static abstract class OnResultCallback {
- /**
- * The result for {@link #getAppPermissions(String, OnResultCallback, Handler)}.
- * @param permissions The permissions list.
- */
- public void onGetAppPermissions(@NonNull
- List<RuntimePermissionPresentationInfo> permissions) {
- /* do nothing - stub */
- }
- }
-
- private static final Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static RuntimePermissionPresenter sInstance;
-
- private final RemoteService mRemoteService;
-
- /**
- * Gets the singleton runtime permission presenter.
- *
- * @param context Context for accessing resources.
- * @return The singleton instance.
- */
- public static RuntimePermissionPresenter getInstance(@NonNull Context context) {
- synchronized (sLock) {
- if (sInstance == null) {
- sInstance = new RuntimePermissionPresenter(context.getApplicationContext());
- }
- return sInstance;
- }
- }
-
- private RuntimePermissionPresenter(Context context) {
- mRemoteService = new RemoteService(context);
- }
-
- /**
- * Gets the runtime permissions for an app.
- *
- * @param packageName The package for which to query.
- * @param callback Callback to receive the result.
- * @param handler Handler on which to invoke the callback.
- */
- public void getAppPermissions(@NonNull String packageName,
- @NonNull OnResultCallback callback, @Nullable Handler handler) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = callback;
- args.arg3 = handler;
- Message message = mRemoteService.obtainMessage(
- RemoteService.MSG_GET_APP_PERMISSIONS, args);
- mRemoteService.processMessage(message);
- }
-
- /**
- * Revoke the permission {@code permissionName} for app {@code packageName}
- *
- * @param packageName The package for which to revoke
- * @param permissionName The permission to revoke
- */
- public void revokeRuntimePermission(String packageName, String permissionName) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = permissionName;
-
- Message message = mRemoteService.obtainMessage(
- RemoteService.MSG_REVOKE_APP_PERMISSIONS, args);
- mRemoteService.processMessage(message);
- }
-
- private static final class RemoteService
- extends Handler implements ServiceConnection {
- private static final long UNBIND_TIMEOUT_MILLIS = 10000;
-
- public static final int MSG_GET_APP_PERMISSIONS = 1;
- public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
- public static final int MSG_UNBIND = 3;
- public static final int MSG_REVOKE_APP_PERMISSIONS = 4;
-
- private final Object mLock = new Object();
-
- private final Context mContext;
-
- @GuardedBy("mLock")
- private final List<Message> mPendingWork = new ArrayList<>();
-
- @GuardedBy("mLock")
- private IRuntimePermissionPresenter mRemoteInstance;
-
- @GuardedBy("mLock")
- private boolean mBound;
-
- public RemoteService(Context context) {
- super(context.getMainLooper(), null, false);
- mContext = context;
- }
-
- public void processMessage(Message message) {
- synchronized (mLock) {
- if (!mBound) {
- Intent intent = new Intent(
- RuntimePermissionPresenterService.SERVICE_INTERFACE);
- intent.setPackage(mContext.getPackageManager()
- .getPermissionControllerPackageName());
- mBound = mContext.bindService(intent, this,
- Context.BIND_AUTO_CREATE);
- }
- mPendingWork.add(message);
- scheduleNextMessageIfNeededLocked();
- }
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
- scheduleNextMessageIfNeededLocked();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mLock) {
- mRemoteInstance = null;
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_GET_APP_PERMISSIONS: {
- SomeArgs args = (SomeArgs) msg.obj;
- final String packageName = (String) args.arg1;
- final OnResultCallback callback = (OnResultCallback) args.arg2;
- final Handler handler = (Handler) args.arg3;
- args.recycle();
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
- try {
- remoteInstance.getAppPermissions(packageName,
- new RemoteCallback(new RemoteCallback.OnResultListener() {
- @Override
- public void onResult(Bundle result) {
- final List<RuntimePermissionPresentationInfo> reportedPermissions;
- List<RuntimePermissionPresentationInfo> permissions = null;
- if (result != null) {
- permissions = result.getParcelableArrayList(KEY_RESULT);
- }
- if (permissions == null) {
- permissions = Collections.emptyList();
- }
- reportedPermissions = permissions;
- if (handler != null) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- callback.onGetAppPermissions(reportedPermissions);
- }
- });
- } else {
- callback.onGetAppPermissions(reportedPermissions);
- }
- }
- }, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
- scheduleUnbind();
- } break;
-
- case MSG_UNBIND: {
- synchronized (mLock) {
- if (mBound) {
- mContext.unbindService(this);
- mBound = false;
- }
- mRemoteInstance = null;
- }
- } break;
-
- case MSG_REVOKE_APP_PERMISSIONS: {
- SomeArgs args = (SomeArgs) msg.obj;
- final String packageName = (String) args.arg1;
- final String permissionName = (String) args.arg2;
- args.recycle();
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
- try {
- remoteInstance.revokeRuntimePermission(packageName, permissionName);
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
- } break;
- }
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
- }
- }
-
- @GuardedBy("mLock")
- private void scheduleNextMessageIfNeededLocked() {
- if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
- Message nextMessage = mPendingWork.remove(0);
- sendMessage(nextMessage);
- }
- }
-
- private void scheduleUnbind() {
- removeMessages(MSG_UNBIND);
- sendEmptyMessageDelayed(MSG_UNBIND, UNBIND_TIMEOUT_MILLIS);
- }
- }
-}
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index f220205..4fc7f5d 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -143,7 +143,6 @@
/**
* contains statistics about a database
*/
- @TestApi
public static class DbStats {
/** name of the database */
public String dbName;
@@ -175,7 +174,6 @@
* return all pager and database stats for the current process.
* @return {@link PagerStats}
*/
- @TestApi
public static PagerStats getDatabaseInfo() {
PagerStats stats = new PagerStats();
nativeGetPagerStats(stats);
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index 1d9330d..9d37d99 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -28,6 +28,21 @@
*/
public interface BiometricFaceConstants {
//
+ // Accessibility constants
+ //
+ /**
+ * Require the user to look at the device during enrollment and
+ * authentication. Note this is to accommodate people who have limited
+ * vision.
+ */
+ public static final int FEATURE_REQUIRE_ATTENTION = 1;
+ /**
+ * Require a diverse set of poses during enrollment. Note this is to
+ * accommodate people with limited mobility.
+ */
+ public static final int FEATURE_REQUIRE_REQUIRE_DIVERSITY = 2;
+
+ //
// Error messages from face authentication hardware during initialization, enrollment,
// authentication or removal. Must agree with the list in HAL h file
//
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index ff58c75..eb3414d 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -152,5 +152,24 @@
Slog.w(TAG, "setActiveUser(): Service not connected");
}
}
+
+ /**
+ * Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ *
+ * @param token an opaque token returned by password confirmation.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void resetTimeout(byte[] token) {
+ if (mService != null) {
+ try {
+ mService.resetTimeout(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "resetTimeout(): Service not connected");
+ }
+ }
}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 53a0761..de828f2 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -48,4 +48,7 @@
// Notify BiometricService when <Biometric>Service is ready to start the prepared client.
// Client lifecycle is still managed in <Biometric>Service.
void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId);
+
+ // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ void resetTimeout(in byte [] token);
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7148b12..5e402c7 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -393,7 +393,7 @@
* doesn't have any recommendation for this use case or the recommended configurations
* are invalid.
*/
- public RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
+ public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
@RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
(usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) ||
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index dc6cffc..448591f 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -968,6 +968,36 @@
public abstract void close();
/**
+ * Checks whether a particular {@link SessionConfiguration} is supported by the camera device.
+ *
+ * <p>This method performs a runtime check of a given {@link SessionConfiguration}. The result
+ * confirms whether or not the passed session configuration can be successfully used to
+ * create a camera capture session using
+ * {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
+ * </p>
+ *
+ * <p>The method can be called at any point before, during and after active capture session.
+ * It must not impact normal camera behavior in any way and must complete significantly
+ * faster than creating a regular or constrained capture session.</p>
+ *
+ * <p>Note that session parameters will be ignored and calls to
+ * {@link SessionConfiguration#setSessionParameters} are not required.</p>
+ *
+ * @return {@code true} if the given session configuration is supported by the camera device
+ * {@code false} otherwise.
+ * @throws UnsupportedOperationException if the query operation is not supported by the camera
+ * device
+ * @throws IllegalArgumentException if the session configuration is invalid
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ */
+ public boolean isSessionConfigurationSupported(
+ @NonNull SessionConfiguration sessionConfig) throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* A callback objects for receiving updates about the state of a camera device.
*
* <p>A callback instance must be provided to the {@link CameraManager#openCamera} method to
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 7810e6c..57b608f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -703,6 +703,17 @@
}
}
+ @Override
+ public boolean isSessionConfigurationSupported(
+ @NonNull SessionConfiguration sessionConfig) throws CameraAccessException,
+ UnsupportedOperationException, IllegalArgumentException {
+ synchronized(mInterfaceLock) {
+ checkIfCameraClosedOrInError();
+
+ return mRemoteDevice.isSessionConfigurationSupported(sessionConfig);
+ }
+ }
+
/**
* For use by backwards-compatibility code only.
*/
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index 1f4ed13..c8ded8d 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -30,9 +30,11 @@
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.SubmitInfo;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.view.Surface;
/**
@@ -181,6 +183,25 @@
}
}
+ public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig)
+ throws CameraAccessException {
+ try {
+ return mRemoteDevice.isSessionConfigurationSupported(sessionConfig);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
+ throw new UnsupportedOperationException("Session configuration query not " +
+ "supported");
+ } else if (e.errorCode == ICameraService.ERROR_ILLEGAL_ARGUMENT) {
+ throw new IllegalArgumentException("Invalid session configuration");
+ }
+
+ throw e;
+ } catch (Throwable t) {
+ CameraManager.throwAsPublicException(t);
+ throw new UnsupportedOperationException("Unexpected exception", t);
+ }
+ }
+
public long flush() throws CameraAccessException {
try {
return mRemoteDevice.flush();
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index bc7b126..123eb8e 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -28,6 +28,7 @@
import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.SubmitInfo;
import android.os.ConditionVariable;
import android.os.IBinder;
@@ -480,6 +481,12 @@
}
@Override
+ public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig) {
+ // TODO: Add support for this in legacy mode
+ throw new UnsupportedOperationException("Session configuration query not supported!");
+ }
+
+ @Override
public void beginConfigure() {
if (DEBUG) {
Log.d(TAG, "beginConfigure called.");
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 83a0228..1b28d61 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -521,9 +521,10 @@
clearState();
}
- private void makeCurrent(EGLSurface surface) {
+ private void makeCurrent(EGLSurface surface)
+ throws LegacyExceptionUtils.BufferQueueAbandonedException {
EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
- checkEglError("makeCurrent");
+ checkEglDrawError("makeCurrent");
}
private boolean swapBuffers(EGLSurface surface)
@@ -557,6 +558,17 @@
}
}
+ private void checkEglDrawError(String msg)
+ throws LegacyExceptionUtils.BufferQueueAbandonedException {
+ int error;
+ if ((error = EGL14.eglGetError()) == EGL14.EGL_BAD_NATIVE_WINDOW) {
+ throw new LegacyExceptionUtils.BufferQueueAbandonedException();
+ }
+ if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
+ throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
+ }
+ }
+
private void checkEglError(String msg) {
int error;
if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
@@ -709,8 +721,14 @@
if (mConversionSurfaces.size() > 0) {
configureEGLPbufferSurfaces(mConversionSurfaces);
}
- makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
+
+ try {
+ makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
mConversionSurfaces.get(0).eglSurface);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
+ }
+
initializeGLState();
mSurfaceTexture = new SurfaceTexture(getTextureId());
@@ -798,9 +816,9 @@
}
for (EGLSurfaceHolder holder : mConversionSurfaces) {
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
- makeCurrent(holder.eglSurface);
// glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
try {
+ makeCurrent(holder.eglSurface);
drawFrame(mSurfaceTexture, holder.width, holder.height,
(mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index 59e4a33..068c0ce 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -17,6 +17,7 @@
package android.hardware.camera2.params;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.ImageFormat;
@@ -173,7 +174,7 @@
*
* @return Use case id.
*/
- public int getRecommendedUseCase() {
+ public @RecommendedUsecase int getRecommendedUseCase() {
return mUsecase;
}
@@ -326,7 +327,7 @@
* @throws IllegalArgumentException if input size does not exist in the return value of
* getHighSpeedVideoSizes
*/
- public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(Size size) {
+ public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(@NonNull Size size) {
return getUnmodifiableRangeSet(mRecommendedMap.getHighSpeedVideoFpsRangesFor(size));
}
@@ -349,14 +350,14 @@
* For further information refer to {@link StreamConfigurationMap#getHighSpeedVideoSizesFor}.
* </p>
*
- * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()}
+ * @param fpsRange one of the FPS ranges returned by {@link #getHighSpeedVideoFpsRanges()}
* @return A non-modifiable set of video sizes to create high speed capture sessions for high
* speed streaming use cases.
*
* @throws IllegalArgumentException if input FPS range does not exist in the return value of
* getHighSpeedVideoFpsRanges
*/
- public @Nullable Set<Size> getHighSpeedVideoSizesFor(Range<Integer> fpsRange) {
+ public @Nullable Set<Size> getHighSpeedVideoSizesFor(@NonNull Range<Integer> fpsRange) {
return getUnmodifiableSizeSet(mRecommendedMap.getHighSpeedVideoSizesFor(fpsRange));
}
@@ -390,10 +391,8 @@
* 0 if the minimum frame duration is not available.
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
- * @throws NullPointerException if {@code size} was {@code null}
- *
*/
- public long getOutputMinFrameDuration(int format, Size size) {
+ public @IntRange(from = 0) long getOutputMinFrameDuration(int format, @NonNull Size size) {
return mRecommendedMap.getOutputMinFrameDuration(format, size);
}
@@ -409,9 +408,8 @@
* @return a stall duration {@code >=} 0 in nanoseconds
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
- * @throws NullPointerException if {@code size} was {@code null}
*/
- public long getOutputStallDuration(int format, Size size) {
+ public @IntRange(from = 0) long getOutputStallDuration(int format, @NonNull Size size) {
return mRecommendedMap.getOutputStallDuration(format, size);
}
@@ -422,16 +420,12 @@
* </p>
*
* @param klass
- * a non-{@code null} {@link Class} object reference
+ * a {@link Class} object reference
* @return
* a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format,
* or {@code null} if the {@code klass} is not a supported output.
- *
- *
- * @throws NullPointerException if {@code klass} was {@code null}
- *
*/
- public <T> @Nullable Set<Size> getOutputSizes(Class<T> klass) {
+ public <T> @Nullable Set<Size> getOutputSizes(@NonNull Class<T> klass) {
if (mSupportsPrivate) {
return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass));
}
@@ -447,16 +441,15 @@
* {@link StreamConfigurationMap#getOutputMinFrameDuration(Class, Size)}.</p>
*
* @param klass
- * a class which has a non-empty array returned by {@link #getOutputSizes(Class)}
+ * a class which has a non-empty array returned by {@link #getOutputSizes(Class)}
* @param size an output-compatible size
* @return a minimum frame duration {@code >} 0 in nanoseconds, or
* 0 if the minimum frame duration is not available.
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
- * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
- *
*/
- public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
+ public <T> @IntRange(from = 0) long getOutputMinFrameDuration(@NonNull final Class<T> klass,
+ @NonNull final Size size) {
if (mSupportsPrivate) {
return mRecommendedMap.getOutputMinFrameDuration(klass, size);
}
@@ -473,14 +466,13 @@
* @param klass
* a class which has a non-empty array returned by {@link #getOutputSizes(Class)}.
* @param size an output-compatible size
- * @return a minimum frame duration {@code >=} 0 in nanoseconds, or 0 if the stall duration is
+ * @return a minimum frame duration {@code >} 0 in nanoseconds, or 0 if the stall duration is
* not available.
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
- * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
- *
*/
- public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
+ public <T> @IntRange(from = 0) long getOutputStallDuration(@NonNull final Class<T> klass,
+ @NonNull final Size size) {
if (mSupportsPrivate) {
return mRecommendedMap.getOutputStallDuration(klass, size);
}
@@ -495,14 +487,13 @@
* <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}.
* </p>
*
- * @param surface a non-{@code null} {@link Surface} object reference
+ * @param surface a {@link Surface} object reference
* @return {@code true} if this is supported, {@code false} otherwise
*
- * @throws NullPointerException if {@code surface} was {@code null}
* @throws IllegalArgumentException if the Surface endpoint is no longer valid
*
*/
- public boolean isOutputSupportedFor(Surface surface) {
+ public boolean isOutputSupportedFor(@NonNull Surface surface) {
return mRecommendedMap.isOutputSupportedFor(surface);
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 8a8afb2..3ea58ad 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -27,6 +27,10 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.utils.HashCodeHelpers;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
import java.util.Collections;
import java.util.List;
@@ -40,7 +44,9 @@
/**
* A helper class that aggregates all supported arguments for capture session initialization.
*/
-public final class SessionConfiguration {
+public final class SessionConfiguration implements Parcelable {
+ private static final String TAG = "SessionConfiguration";
+
/**
* A regular session type containing instances of {@link OutputConfiguration} running
* at regular non high speed FPS ranges and optionally {@link InputConfiguration} for
@@ -110,6 +116,108 @@
}
/**
+ * Create a SessionConfiguration from Parcel.
+ * No support for parcelable 'mStateCallback', 'mExecutor' and 'mSessionParameters' yet.
+ */
+ private SessionConfiguration(@NonNull Parcel source) {
+ int sessionType = source.readInt();
+ int inputWidth = source.readInt();
+ int inputHeight = source.readInt();
+ int inputFormat = source.readInt();
+ ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>();
+ source.readTypedList(outConfigs, OutputConfiguration.CREATOR);
+
+ if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) {
+ mInputConfig = new InputConfiguration(inputWidth, inputHeight, inputFormat);
+ }
+ mSessionType = sessionType;
+ mOutputConfigurations = outConfigs;
+ }
+
+ public static final Parcelable.Creator<SessionConfiguration> CREATOR =
+ new Parcelable.Creator<SessionConfiguration> () {
+ @Override
+ public SessionConfiguration createFromParcel(Parcel source) {
+ try {
+ SessionConfiguration sessionConfiguration = new SessionConfiguration(source);
+ return sessionConfiguration;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating SessionConfiguration from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public SessionConfiguration[] newArray(int size) {
+ return new SessionConfiguration[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (dest == null) {
+ throw new IllegalArgumentException("dest must not be null");
+ }
+ dest.writeInt(mSessionType);
+ if (mInputConfig != null) {
+ dest.writeInt(mInputConfig.getWidth());
+ dest.writeInt(mInputConfig.getHeight());
+ dest.writeInt(mInputConfig.getFormat());
+ } else {
+ dest.writeInt(/*inputWidth*/ 0);
+ dest.writeInt(/*inputHeight*/ 0);
+ dest.writeInt(/*inputFormat*/ -1);
+ }
+ dest.writeTypedList(mOutputConfigurations);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Check if this {@link SessionConfiguration} is equal to another {@link SessionConfiguration}.
+ *
+ * <p>Two output session configurations are only equal if and only if the underlying input
+ * configuration, output configurations, and session type are equal. </p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ } else if (this == obj) {
+ return true;
+ } else if (obj instanceof SessionConfiguration) {
+ final SessionConfiguration other = (SessionConfiguration) obj;
+ if (mInputConfig != other.mInputConfig || mSessionType != other.mSessionType ||
+ mOutputConfigurations.size() != other.mOutputConfigurations.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < mOutputConfigurations.size(); i++) {
+ if (!mOutputConfigurations.get(i).equals(other.mOutputConfigurations.get(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(), mInputConfig.hashCode(),
+ mSessionType);
+ }
+
+ /**
* Retrieve the type of the capture session.
*
* @return The capture session type.
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index 0a76c2b..a4c1332 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -16,20 +16,81 @@
package android.hardware.display;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
import com.android.internal.R;
/**
* Manages the display's color transforms and modes.
+ *
* @hide
*/
+@SystemService(Context.COLOR_DISPLAY_SERVICE)
public final class ColorDisplayManager {
+ private final ColorDisplayManagerInternal mManager;
+
+ /**
+ * @hide
+ */
+ public ColorDisplayManager() {
+ mManager = ColorDisplayManagerInternal.getInstance();
+ }
+
+ /**
+ * Returns whether the device has a wide color gamut display.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean isDeviceColorManaged() {
+ return mManager.isDeviceColorManaged();
+ }
+
/**
* Returns {@code true} if Night Display is supported by the device.
*/
public static boolean isNightDisplayAvailable(Context context) {
return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
}
+
+ private static class ColorDisplayManagerInternal {
+
+ private static ColorDisplayManagerInternal sInstance;
+
+ private final IColorDisplayManager mCdm;
+
+ private ColorDisplayManagerInternal(IColorDisplayManager colorDisplayManager) {
+ mCdm = colorDisplayManager;
+ }
+
+ public static ColorDisplayManagerInternal getInstance() {
+ synchronized (ColorDisplayManagerInternal.class) {
+ if (sInstance == null) {
+ try {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.COLOR_DISPLAY_SERVICE);
+ sInstance = new ColorDisplayManagerInternal(
+ IColorDisplayManager.Stub.asInterface(b));
+ } catch (ServiceNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ boolean isDeviceColorManaged() {
+ try {
+ return mCdm.isDeviceColorManaged();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 9d61f02..1af9cde 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.annotation.Nullable;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
@@ -195,6 +196,44 @@
public abstract void onOverlayChanged();
/**
+ * Get the attributes available for display color sampling.
+ * @param displayId id of the display to collect the sample from.
+ *
+ * @return The attributes the display supports, or null if sampling is not supported.
+ */
+ @Nullable
+ public abstract DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+ int displayId);
+
+ /**
+ * Enable or disable the collection of color samples.
+ *
+ * @param displayId id of the display to collect the sample from.
+ * @param componentMask a bitmask of the color channels to collect samples for, or zero for all
+ * available.
+ * @param maxFrames maintain a ringbuffer of the last maxFrames.
+ * @param enable True to enable, False to disable.
+ *
+ * @return True if sampling was enabled, false if failure.
+ */
+ public abstract boolean setDisplayedContentSamplingEnabled(
+ int displayId, boolean enable, int componentMask, int maxFrames);
+
+ /**
+ * Accesses the color histogram statistics of displayed frames on devices that support sampling.
+ *
+ * @param displayId id of the display to collect the sample from
+ * @param maxFrames limit the statistics to the last maxFrames number of frames.
+ * @param timestamp discard statistics that were collected prior to timestamp, where timestamp
+ * is given as CLOCK_MONOTONIC.
+ * @return The statistics representing a histogram of the color distribution of the frames
+ * displayed on-screen, or null if sampling is not supported.
+ */
+ @Nullable
+ public abstract DisplayedContentSample getDisplayedContentSample(
+ int displayId, long maxFrames, long timestamp);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/display/DisplayedContentSample.java b/core/java/android/hardware/display/DisplayedContentSample.java
new file mode 100644
index 0000000..0610377
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayedContentSample.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+/**
+ * @hide
+ */
+public final class DisplayedContentSample {
+ private long mNumFrames;
+ private long[] mSamplesComponent0;
+ private long[] mSamplesComponent1;
+ private long[] mSamplesComponent2;
+ private long[] mSamplesComponent3;
+
+ /**
+ * Construct an object representing a color histogram of pixels that were displayed on screen.
+ *
+ * @param numFrames The number of frames represented by this sample.
+ * @param mSamplesComponent0 is a histogram counting how many times a pixel of a given value
+ * was displayed onscreen for FORMAT_COMPONENT_0. The buckets of the histogram are evenly
+ * weighted, the number of buckets is device specific.
+ * eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that 10 red pixels were
+ * displayed onscreen in range 0x00->0x3F, 6 red pixels were displayed onscreen in range
+ * 0x40->0x7F, etc.
+ * @param mSamplesComponent1 is the same sample definition as sampleComponent0, but for the
+ * second component of format.
+ * @param mSamplesComponent2 is the same sample definition as sampleComponent0, but for the
+ * third component of format.
+ * @param mSamplesComponent3 is the same sample definition as sampleComponent0, but for the
+ * fourth component of format.
+ */
+ public DisplayedContentSample(long numFrames,
+ long[] sampleComponent0,
+ long[] sampleComponent1,
+ long[] sampleComponent2,
+ long[] sampleComponent3) {
+ mNumFrames = numFrames;
+ mSamplesComponent0 = sampleComponent0;
+ mSamplesComponent1 = sampleComponent1;
+ mSamplesComponent2 = sampleComponent2;
+ mSamplesComponent3 = sampleComponent3;
+ }
+
+ public enum ColorComponent {
+ CHANNEL0,
+ CHANNEL1,
+ CHANNEL2,
+ CHANNEL3,
+ }
+
+ /**
+ * Returns a color histogram according to component channel.
+ *
+ * @param component the component to return, according to the PixelFormat ordering
+ * (eg, for RGBA, CHANNEL0 is R, CHANNEL1 is G, etc).
+ *
+ * @return an evenly weighted histogram counting how many times a pixel was
+ * displayed onscreen that fell into the corresponding bucket, with the first entry
+ * corresponding to the normalized 0.0 value, and the last corresponding to the 1.0
+ * value for that PixelFormat component.
+ */
+ public long[] getSampleComponent(ColorComponent component) {
+ switch (component) {
+ case CHANNEL0: return mSamplesComponent0;
+ case CHANNEL1: return mSamplesComponent1;
+ case CHANNEL2: return mSamplesComponent2;
+ case CHANNEL3: return mSamplesComponent3;
+ default: throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Return the number of frames this sample was collected over.
+ *
+ * @return the number of frames that this sample was collected over.
+ */
+ public long getNumFrames() {
+ return mNumFrames;
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java b/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java
new file mode 100644
index 0000000..aad68d9
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+/**
+ * @hide
+ */
+public final class DisplayedContentSamplingAttributes {
+ private int mPixelFormat;
+ private int mDataspace;
+ private int mComponentMask;
+
+ /* Creates the attributes reported by the display hardware about what capabilities
+ * are present.
+ *
+ * NOTE: the format and ds constants must match the values from graphics/common/x.x/types.hal
+ * @param format the format that the display hardware samples in.
+ * @param ds the dataspace in use when sampling.
+ * @param componentMask a mask of which of the format components are supported.
+ */
+ public DisplayedContentSamplingAttributes(int format, int ds, int componentMask) {
+ mPixelFormat = format;
+ mDataspace = ds;
+ mComponentMask = componentMask;
+ }
+
+ /* Returns the pixel format that the display hardware uses when sampling.
+ *
+ * NOTE: the returned constant matches the values from graphics/common/x.x/types.hal
+ * @return the format that the samples were collected in.
+ */
+ public int getPixelFormat() {
+ return mPixelFormat;
+ }
+
+ /* Returns the dataspace that the display hardware uses when sampling.
+ *
+ * NOTE: the returned constant matches the values from graphics/common/x.x/types.hal
+ * @return the dataspace that the samples were collected in.
+ */
+ public int getDataspace() {
+ return mDataspace;
+ }
+
+ /* Returns a mask of which components can be collected by the sampling engine.
+ *
+ * @return a mask of the components which are supported by the engine. The lowest
+ * bit corresponds to the lowest component (ie, 0x1 corresponds to A for RGBA).
+ */
+ public int getComponentMask() {
+ return mComponentMask;
+ }
+}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
similarity index 83%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to core/java/android/hardware/display/IColorDisplayManager.aidl
index c66a6cb..f786589 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package android.hardware.display;
-parcelable ContentCaptureEvent;
+/** @hide */
+interface IColorDisplayManager {
+ boolean isDeviceColorManaged();
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 322863a..bac23b3 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -207,11 +207,8 @@
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void enroll(byte[] token, CancellationSignal cancel, int flags,
- int userId, EnrollmentCallback callback) {
- if (userId == UserHandle.USER_CURRENT) {
- userId = getCurrentUserId();
- }
+ public void enroll(byte[] token, CancellationSignal cancel,
+ EnrollmentCallback callback, int[] disabledFeatures) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
@@ -228,8 +225,8 @@
if (mService != null) {
try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, token, userId, mServiceReceiver, flags,
- mContext.getOpPackageName());
+ mService.enroll(mToken, token, mServiceReceiver,
+ mContext.getOpPackageName(), disabledFeatures);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
@@ -284,10 +281,10 @@
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void setRequireAttention(boolean requireAttention, byte[] token) {
+ public void setFeature(int feature, boolean enabled, byte[] token) {
if (mService != null) {
try {
- mService.setRequireAttention(requireAttention, token);
+ mService.setFeature(feature, enabled, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -298,11 +295,11 @@
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public boolean getRequireAttention(byte[] token) {
+ public boolean getFeature(int feature) {
boolean result = true;
if (mService != null) {
try {
- mService.getRequireAttention(token);
+ result = mService.getFeature(feature);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index a15dcec..a1c88f8 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -50,8 +50,8 @@
int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start face enrollment
- void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
- int flags, String opPackageName);
+ void enroll(IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+ String opPackageName, in int [] disabledFeatures);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
@@ -98,9 +98,9 @@
// Enumerate all faces
void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver);
- int setRequireAttention(boolean requireAttention, in byte [] token);
+ int setFeature(int feature, boolean enabled, in byte [] token);
- boolean getRequireAttention(in byte [] token);
+ boolean getFeature(int feature);
void userActivity();
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0bdfca7..8a5f43d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -79,6 +80,7 @@
mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
mNetworkSpecifier = null;
+ mTransportInfo = null;
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
mEstablishingVpnAppUid = INVALID_UID;
@@ -95,6 +97,7 @@
mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
+ mTransportInfo = nc.mTransportInfo;
mSignalStrength = nc.mSignalStrength;
setUids(nc.mUids); // Will make the defensive copy
mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
@@ -874,6 +877,7 @@
}
private NetworkSpecifier mNetworkSpecifier = null;
+ private TransportInfo mTransportInfo = null;
/**
* Sets the optional bearer specific network specifier.
@@ -899,6 +903,19 @@
}
/**
+ * Sets the optional transport specific information.
+ *
+ * @param transportInfo A concrete, parcelable framework class that extends
+ * {@link TransportInfo}.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ public NetworkCapabilities setTransportInfo(TransportInfo transportInfo) {
+ mTransportInfo = transportInfo;
+ return this;
+ }
+
+ /**
* Gets the optional bearer specific network specifier.
*
* @return The optional {@link NetworkSpecifier} specifying the bearer specific network
@@ -910,6 +927,19 @@
return mNetworkSpecifier;
}
+ /**
+ * Returns a transport-specific information container. The application may cast this
+ * container to a concrete sub-class based on its knowledge of the network request. The
+ * application should be able to deal with a {@code null} return value or an invalid case,
+ * e.g. use {@code instanceof} operator to verify expected type.
+ *
+ * @return A concrete implementation of the {@link TransportInfo} class or null if not
+ * available for the network.
+ */
+ @Nullable public TransportInfo getTransportInfo() {
+ return mTransportInfo;
+ }
+
private void combineSpecifiers(NetworkCapabilities nc) {
if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
throw new IllegalStateException("Can't combine two networkSpecifiers");
@@ -926,6 +956,17 @@
return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
}
+ private void combineTransportInfos(NetworkCapabilities nc) {
+ if (mTransportInfo != null && !mTransportInfo.equals(nc.mTransportInfo)) {
+ throw new IllegalStateException("Can't combine two TransportInfos");
+ }
+ setTransportInfo(nc.mTransportInfo);
+ }
+
+ private boolean equalsTransportInfo(NetworkCapabilities nc) {
+ return Objects.equals(mTransportInfo, nc.mTransportInfo);
+ }
+
/**
* Magic value that indicates no signal strength provided. A request specifying this value is
* always satisfied.
@@ -1238,6 +1279,7 @@
combineTransportTypes(nc);
combineLinkBandwidths(nc);
combineSpecifiers(nc);
+ combineTransportInfos(nc);
combineSignalStrength(nc);
combineUids(nc);
combineSSIDs(nc);
@@ -1347,6 +1389,7 @@
&& equalsLinkBandwidths(that)
&& equalsSignalStrength(that)
&& equalsSpecifier(that)
+ && equalsTransportInfo(that)
&& equalsUids(that)
&& equalsSSID(that));
}
@@ -1364,7 +1407,8 @@
+ Objects.hashCode(mNetworkSpecifier) * 23
+ (mSignalStrength * 29)
+ Objects.hashCode(mUids) * 31
- + Objects.hashCode(mSSID) * 37;
+ + Objects.hashCode(mSSID) * 37
+ + Objects.hashCode(mTransportInfo) * 41;
}
@Override
@@ -1379,6 +1423,7 @@
dest.writeInt(mLinkUpBandwidthKbps);
dest.writeInt(mLinkDownBandwidthKbps);
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
+ dest.writeParcelable((Parcelable) mTransportInfo, flags);
dest.writeInt(mSignalStrength);
dest.writeArraySet(mUids);
dest.writeString(mSSID);
@@ -1396,6 +1441,7 @@
netCap.mLinkUpBandwidthKbps = in.readInt();
netCap.mLinkDownBandwidthKbps = in.readInt();
netCap.mNetworkSpecifier = in.readParcelable(null);
+ netCap.mTransportInfo = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
null /* ClassLoader, null for default */);
@@ -1435,6 +1481,9 @@
if (mNetworkSpecifier != null) {
sb.append(" Specifier: <").append(mNetworkSpecifier).append(">");
}
+ if (mTransportInfo != null) {
+ sb.append(" TransportInfo: <").append(mTransportInfo).append(">");
+ }
if (hasSignalStrength()) {
sb.append(" SignalStrength: ").append(mSignalStrength);
}
@@ -1501,6 +1550,9 @@
if (mNetworkSpecifier != null) {
proto.write(NetworkCapabilitiesProto.NETWORK_SPECIFIER, mNetworkSpecifier.toString());
}
+ if (mTransportInfo != null) {
+ // TODO b/120653863: write transport-specific info to proto?
+ }
proto.write(NetworkCapabilitiesProto.CAN_REPORT_SIGNAL_STRENGTH, hasSignalStrength());
proto.write(NetworkCapabilitiesProto.SIGNAL_STRENGTH, mSignalStrength);
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 5447f59..a00b9a3 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -44,6 +44,7 @@
*
* @hide
*/
+// @NotThreadSafe
public class NetworkStats implements Parcelable {
private static final String TAG = "NetworkStats";
/** {@link #iface} value when interface details unavailable. */
@@ -443,6 +444,26 @@
return entry;
}
+ /**
+ * If @{code dest} is not equal to @{code src}, copy entry from index @{code src} to index
+ * @{code dest}.
+ */
+ private void maybeCopyEntry(int dest, int src) {
+ if (dest == src) return;
+ iface[dest] = iface[src];
+ uid[dest] = uid[src];
+ set[dest] = set[src];
+ tag[dest] = tag[src];
+ metered[dest] = metered[src];
+ roaming[dest] = roaming[src];
+ defaultNetwork[dest] = defaultNetwork[src];
+ rxBytes[dest] = rxBytes[src];
+ rxPackets[dest] = rxPackets[src];
+ txBytes[dest] = txBytes[src];
+ txPackets[dest] = txPackets[src];
+ operations[dest] = operations[src];
+ }
+
public long getElapsedRealtime() {
return elapsedRealtime;
}
@@ -941,21 +962,18 @@
}
/**
- * Return all rows except those attributed to the requested UID; doesn't
- * mutate the original structure.
+ * Remove all rows that match one of specified UIDs.
*/
- public NetworkStats withoutUids(int[] uids) {
- final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
-
- Entry entry = new Entry();
+ public void removeUids(int[] uids) {
+ int nextOutputEntry = 0;
for (int i = 0; i < size; i++) {
- entry = getValues(i, entry);
- if (!ArrayUtils.contains(uids, entry.uid)) {
- stats.addValues(entry);
+ if (!ArrayUtils.contains(uids, uid[i])) {
+ maybeCopyEntry(nextOutputEntry, i);
+ nextOutputEntry++;
}
}
- return stats;
+ size = nextOutputEntry;
}
/**
diff --git a/core/java/android/net/PrivateDnsConnectivityChecker.java b/core/java/android/net/PrivateDnsConnectivityChecker.java
new file mode 100644
index 0000000..cfd458c
--- /dev/null
+++ b/core/java/android/net/PrivateDnsConnectivityChecker.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Class for testing connectivity to DNS-over-TLS servers.
+ * {@hide}
+ */
+public class PrivateDnsConnectivityChecker {
+ private static final String TAG = "NetworkUtils";
+
+ private static final int PRIVATE_DNS_PORT = 853;
+ private static final int CONNECTION_TIMEOUT_MS = 5000;
+
+ private PrivateDnsConnectivityChecker() { }
+
+ /**
+ * checks that a provided host can perform a TLS handshake on port 853.
+ * @param hostname host to connect to.
+ */
+ public static boolean canConnectToPrivateDnsServer(@NonNull String hostname) {
+ final SocketFactory factory = SSLSocketFactory.getDefault();
+ TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_APP);
+
+ try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
+ socket.setSoTimeout(CONNECTION_TIMEOUT_MS);
+ socket.connect(new InetSocketAddress(hostname, PRIVATE_DNS_PORT));
+ if (!socket.isConnected()) {
+ Log.w(TAG, String.format("Connection to %s failed.", hostname));
+ return false;
+ }
+ socket.startHandshake();
+ Log.w(TAG, String.format("TLS handshake to %s succeeded.", hostname));
+ return true;
+ } catch (IOException e) {
+ Log.w(TAG, String.format("TLS handshake to %s failed.", hostname), e);
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 22dd4fc..bbf8f97 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -274,7 +274,6 @@
* Changes only take effect during subsequent calls to
* {@link #tagSocket(Socket)}.
*/
- @SystemApi
@SuppressLint("Doclava125")
public static void setThreadStatsUid(int uid) {
NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
@@ -313,7 +312,6 @@
*
* @see #setThreadStatsUid(int)
*/
- @SystemApi
@SuppressLint("Doclava125")
public static void clearThreadStatsUid() {
NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/android/net/TransportInfo.java
similarity index 69%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to core/java/android/net/TransportInfo.java
index c66a6cb..b78d3fe 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/java/android/net/TransportInfo.java
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package android.net;
-parcelable ContentCaptureEvent;
+/**
+ * A container for transport-specific capabilities which is returned by
+ * {@link NetworkCapabilities#getTransportInfo()}. Specific networks
+ * may provide concrete implementations of this interface.
+ */
+public interface TransportInfo {
+}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index d09f33b..af3ee09 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -374,11 +374,12 @@
public abstract String toString();
/**
- * Return a string representation of the URI that is safe to print
- * to logs and other places where PII should be avoided.
- * @hide
+ * Return a string representation of this URI that has common forms of PII redacted,
+ * making it safer to use for logging purposes. For example, {@code tel:800-466-4411} is
+ * 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
*/
- @UnsupportedAppUsage
public String toSafeString() {
String scheme = getScheme();
String ssp = getSchemeSpecificPart();
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index f9b6dfc..280dad0 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -16,7 +16,6 @@
package android.net.http;
-import android.annotation.SystemApi;
import android.security.net.config.UserCertificateSource;
import com.android.org.conscrypt.TrustManagerImpl;
@@ -133,7 +132,6 @@
* Returns {@code true} if the TrustManager uses the same trust configuration for the provided
* hostnames.
*/
- @SystemApi
public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
if (mIsSameTrustConfiguration == null) {
return true;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 64314a7..9939a3c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
@@ -85,6 +86,15 @@
public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE
/**
+ * Value to represents that a calling work source is not set.
+ *
+ * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource.
+ *
+ * @hide
+ */
+ public static final int UNSET_WORKSOURCE = -1;
+
+ /**
* Control whether dump() calls are allowed.
*/
private static volatile String sDumpDisabled = null;
@@ -390,6 +400,9 @@
* reasons, we only support one UID. This UID represents the original user responsible for the
* binder calls.
*
+ * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after setting the
+ * worksource.
+ *
* <p>A typical use case would be
* <pre>
* long token = Binder.setCallingWorkSourceUid(uid);
@@ -408,6 +421,7 @@
* @hide
**/
@CriticalNative
+ @SystemApi
public static final native long setCallingWorkSourceUid(int workSource);
/**
@@ -421,6 +435,7 @@
* @hide
*/
@CriticalNative
+ @SystemApi
public static final native int getCallingWorkSourceUid();
/**
@@ -429,10 +444,24 @@
* <p>The work source will be propagated for future outgoing binder transactions
* executed on this thread.
*
+ * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after clearing the
+ * worksource.
+ *
+ * <p>A typical use case would be
+ * <pre>
+ * long token = Binder.clearCallingWorkSource();
+ * try {
+ * // Call an API.
+ * } finally {
+ * Binder.restoreCallingWorkSource(token);
+ * }
+ * </pre>
+ *
* @return token to restore original work source.
* @hide
**/
@CriticalNative
+ @SystemApi
public static final native long clearCallingWorkSource();
/**
@@ -449,11 +478,10 @@
* }
* </pre>
*
- * <p>The work source will be propagated for future outgoing binder transactions
- * executed on this thread.
* @hide
**/
@CriticalNative
+ @SystemApi
public static final native void restoreCallingWorkSource(long token);
/**
@@ -594,6 +622,7 @@
* See {@link setProxyTransactListener}.
* @hide
*/
+ @SystemApi
public interface ProxyTransactListener {
/**
* Called before onTransact.
@@ -656,6 +685,7 @@
* <li>Never execute another binder transaction inside the listener.
* @hide
*/
+ @SystemApi
public static void setProxyTransactListener(@Nullable ProxyTransactListener listener) {
BinderProxy.setTransactListener(listener);
}
@@ -909,13 +939,49 @@
private static native long getNativeBBinderHolder();
private static native long getFinalizer();
+ /**
+ * By default, we use the calling uid since we can always trust it.
+ */
+ private static volatile BinderInternal.WorkSourceProvider sWorkSourceProvider =
+ Binder::getCallingUid;
+
+ /**
+ * Sets the work source provider.
+ *
+ * <li>The callback is global. Only fast operations should be done to avoid thread
+ * contentions.
+ * <li>The callback implementation needs to handle synchronization if needed. The methods on the
+ * callback can be called concurrently.
+ * <li>The callback is called on the critical path of the binder transaction so be careful about
+ * performance.
+ * <li>Never execute another binder transaction inside the callback.
+ * @hide
+ */
+ public static void setWorkSourceProvider(BinderInternal.WorkSourceProvider workSourceProvider) {
+ if (workSourceProvider == null) {
+ throw new IllegalArgumentException("workSourceProvider cannot be null");
+ }
+ sWorkSourceProvider = workSourceProvider;
+ }
+
// Entry point from android_util_Binder.cpp's onTransact
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
+ final int workSourceUid = sWorkSourceProvider.resolveWorkSourceUid();
+ final long origWorkSource = ThreadLocalWorkSource.setUid(workSourceUid);
+ try {
+ return execTransactInternal(code, dataObj, replyObj, flags, workSourceUid);
+ } finally {
+ ThreadLocalWorkSource.restore(origWorkSource);
+ }
+ }
+
+ private boolean execTransactInternal(int code, long dataObj, long replyObj,
+ int flags, int workSourceUid) {
// Make sure the observer won't change while processing a transaction.
final BinderInternal.Observer observer = sObserver;
final CallSession callSession =
- observer != null ? observer.callStarted(this, code) : null;
+ observer != null ? observer.callStarted(this, code, workSourceUid) : null;
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
// theoretically, we should call transact, which will call onTransact,
@@ -925,7 +991,6 @@
// Log any exceptions as warnings, don't silently suppress them.
// If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
final boolean tracingEnabled = Binder.isTracingEnabled();
- final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
try {
if (tracingEnabled) {
final String transactionName = getTransactionName(code);
@@ -952,14 +1017,14 @@
}
res = true;
} finally {
- ThreadLocalWorkSource.restore(origWorkSource);
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}
+ if (observer != null) {
+ observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
+ }
}
checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
- int replySizeBytes = reply.dataSize();
- int requestSizeBytes = data.dataSize();
reply.recycle();
data.recycle();
@@ -969,10 +1034,6 @@
// to the main transaction loop to wait for another incoming transaction. Either
// way, strict mode begone!
StrictMode.clearGatheredViolations();
- if (observer != null) {
- observer.callEnded(callSession, requestSizeBytes, replySizeBytes);
- }
-
return res;
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
old mode 100644
new mode 100755
index 292543c..9fea873
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1226,7 +1226,8 @@
* null (if, for instance, the radio is not currently on).
*/
public static String getRadioVersion() {
- return SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION, null);
+ String propVal = SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION);
+ return TextUtils.isEmpty(propVal) ? null : propVal;
}
private static String getString(String property) {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 0c1aae8..8904ee6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -657,6 +657,12 @@
public static String DIRECTORY_SCREENSHOTS = "Screenshots";
/**
+ * Standard directory in which to place any audio files which are
+ * audiobooks.
+ */
+ public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
+
+ /**
* List of standard storage directories.
* <p>
* Each of its values have its own constant:
@@ -671,6 +677,7 @@
* <li>{@link #DIRECTORY_DOWNLOADS}
* <li>{@link #DIRECTORY_DCIM}
* <li>{@link #DIRECTORY_DOCUMENTS}
+ * <li>{@link #DIRECTORY_AUDIOBOOKS}
* </ul>
* @hide
*/
@@ -684,7 +691,8 @@
DIRECTORY_MOVIES,
DIRECTORY_DOWNLOADS,
DIRECTORY_DCIM,
- DIRECTORY_DOCUMENTS
+ DIRECTORY_DOCUMENTS,
+ DIRECTORY_AUDIOBOOKS,
};
/**
@@ -709,6 +717,7 @@
/** {@hide} */ public static final int HAS_DOWNLOADS = 1 << 7;
/** {@hide} */ public static final int HAS_DCIM = 1 << 8;
/** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
+ /** {@hide} */ public static final int HAS_AUDIOBOOKS = 1 << 10;
/** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
/** {@hide} */ public static final int HAS_OTHER = 1 << 17;
@@ -738,6 +747,7 @@
else if (DIRECTORY_DOWNLOADS.equals(name)) res |= HAS_DOWNLOADS;
else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
+ else if (DIRECTORY_AUDIOBOOKS.equals(name)) res |= HAS_AUDIOBOOKS;
else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
else res |= HAS_OTHER;
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 28ea553..567efa7 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -66,6 +66,9 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
@@ -717,6 +720,57 @@
}
/**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(@NonNull File file, @NonNull String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ try (FileInputStream in = new FileInputStream(file)) {
+ return digest(in, algorithm);
+ }
+ }
+
+ /**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(@NonNull InputStream in, @NonNull String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ // TODO: implement kernel optimizations
+ return digestInternalUserspace(in, algorithm);
+ }
+
+ /**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(FileDescriptor fd, String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ // TODO: implement kernel optimizations
+ return digestInternalUserspace(new FileInputStream(fd), algorithm);
+ }
+
+ private static byte[] digestInternalUserspace(InputStream in, String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ final MessageDigest digest = MessageDigest.getInstance(algorithm);
+ try (DigestInputStream digestStream = new DigestInputStream(in, digest)) {
+ final byte[] buffer = new byte[8192];
+ while (digestStream.read(buffer) != -1) {
+ }
+ }
+ return digest.digest();
+ }
+
+ /**
* Delete older files in a directory until only those matching the given
* constraints remain.
*
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 900b62d..124d7b1 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,8 +22,6 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.opengl.EGL14;
-import android.os.Build;
-import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
@@ -37,6 +35,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/** @hide */
public class GraphicsEnvironment {
@@ -67,7 +70,7 @@
*/
public void setup(Context context, Bundle coreSettings) {
setupGpuLayers(context, coreSettings);
- setupAngle(context, coreSettings);
+ setupAngle(context, coreSettings, context.getPackageName());
chooseDriver(context, coreSettings);
}
@@ -192,24 +195,95 @@
setLayerPaths(mClassLoader, layerPaths);
}
+ enum OpenGlDriverChoice {
+ DEFAULT,
+ NATIVE,
+ ANGLE
+ }
+
+ private static final Map<OpenGlDriverChoice, String> sDriverMap = buildMap();
+ private static Map<OpenGlDriverChoice, String> buildMap() {
+ Map<OpenGlDriverChoice, String> map = new HashMap<>();
+ map.put(OpenGlDriverChoice.DEFAULT, "default");
+ map.put(OpenGlDriverChoice.ANGLE, "angle");
+ map.put(OpenGlDriverChoice.NATIVE, "native");
+
+ return map;
+ }
+
+
+ private static List<String> getGlobalSettingsString(Bundle bundle, String globalSetting) {
+ List<String> valueList = null;
+ String settingsValue = bundle.getString(globalSetting);
+
+ if (settingsValue != null) {
+ valueList = new ArrayList<>(Arrays.asList(settingsValue.split(",")));
+ } else {
+ valueList = new ArrayList<>();
+ }
+
+ return valueList;
+ }
+
+ private static int getGlobalSettingsPkgIndex(String pkgName,
+ List<String> globalSettingsDriverPkgs) {
+ for (int pkgIndex = 0; pkgIndex < globalSettingsDriverPkgs.size(); pkgIndex++) {
+ if (globalSettingsDriverPkgs.get(pkgIndex).equals(pkgName)) {
+ return pkgIndex;
+ }
+ }
+
+ return -1;
+ }
+
+ private static String getDriverForPkg(Bundle bundle, String packageName) {
+ String allUseAngle =
+ bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+ if ((allUseAngle != null) && allUseAngle.equals("1")) {
+ return sDriverMap.get(OpenGlDriverChoice.ANGLE);
+ }
+
+ List<String> globalSettingsDriverPkgs =
+ getGlobalSettingsString(bundle,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS);
+ List<String> globalSettingsDriverValues =
+ getGlobalSettingsString(bundle,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
+
+ // Make sure we have a good package name
+ if ((packageName == null) || (packageName.isEmpty())) {
+ return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+ }
+ // Make sure we have good settings to use
+ if (globalSettingsDriverPkgs.isEmpty() || globalSettingsDriverValues.isEmpty()
+ || (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size())) {
+ Log.w(TAG,
+ "Global.Settings values are invalid: "
+ + "globalSettingsDriverPkgs.size = "
+ + globalSettingsDriverPkgs.size() + ", "
+ + "globalSettingsDriverValues.size = "
+ + globalSettingsDriverValues.size());
+ return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+ }
+
+ int pkgIndex = getGlobalSettingsPkgIndex(packageName, globalSettingsDriverPkgs);
+
+ if (pkgIndex < 0) {
+ return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+ }
+
+ return globalSettingsDriverValues.get(pkgIndex);
+ }
+
/**
* Pass ANGLE details down to trigger enable logic
*/
- private static void setupAngle(Context context, Bundle coreSettings) {
+ private void setupAngle(Context context, Bundle bundle, String packageName) {
+ String devOptIn = getDriverForPkg(bundle, packageName);
- String angleEnabledApp =
- coreSettings.getString(Settings.Global.ANGLE_ENABLED_APP);
-
- String packageName = context.getPackageName();
-
- boolean devOptIn = false;
- if ((angleEnabledApp != null && packageName != null)
- && (!angleEnabledApp.isEmpty() && !packageName.isEmpty())
- && angleEnabledApp.equals(packageName)) {
-
- Log.i(TAG, packageName + " opted in for ANGLE via Developer Setting");
-
- devOptIn = true;
+ if (DEBUG) {
+ Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+ + "set to: '" + devOptIn + "'");
}
ApplicationInfo angleInfo;
@@ -269,7 +343,7 @@
}
// If no temp rules, load the real ones from the APK
- if (rulesFd == null) {
+ if (DEBUG && (rulesFd == null)) {
// Pass the rules file to loader for ANGLE decisions
AssetManager angleAssets = null;
@@ -303,8 +377,7 @@
// Further opt-in logic is handled in native, so pass relevant info down
// TODO: Move the ANGLE selection logic earlier so we don't need to keep these
// file descriptors open.
- setAngleInfo(paths, packageName, devOptIn,
- rulesFd, rulesOffset, rulesLength);
+ setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
}
/**
@@ -452,6 +525,6 @@
private static native void setDebugLayersGLES(String layers);
private static native void setDriverPath(String path);
private static native void setAngleInfo(String path, String appPackage,
- boolean devOptIn, FileDescriptor rulesFd,
+ String devOptIn, FileDescriptor rulesFd,
long rulesOffset, long rulesLength);
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 228fe7a..3de3494 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -32,10 +32,7 @@
/**
* Create and initialize a HwBinder object and the native objects
* used to allow this to participate in hwbinder transactions.
- *
- * @hide
*/
- @SystemApi
public HwBinder() {
native_setup();
@@ -44,7 +41,6 @@
mNativeContext);
}
- /** @hide */
@Override
public final native void transact(
int code, HwParcel request, HwParcel reply, int flags)
@@ -57,10 +53,7 @@
* @param request parceled transaction
* @param reply object to parcel reply into
* @param flags transaction flags to be chosen by wire protocol
- *
- * @hide
*/
- @SystemApi
public abstract void onTransact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
@@ -69,9 +62,7 @@
* Registers this service with the hwservicemanager.
*
* @param serviceName instance name of the service
- * @hide
*/
- @SystemApi
public native final void registerService(String serviceName)
throws RemoteException;
@@ -81,9 +72,7 @@
* @param iface fully-qualified interface name for example foo.bar@1.3::IBaz
* @param serviceName the instance name of the service for example default.
* @throws NoSuchElementException when the service is unavailable
- * @hide
*/
- @SystemApi
public static final IHwBinder getService(
String iface,
String serviceName)
@@ -96,9 +85,7 @@
* @param serviceName the instance name of the service for example default.
* @param retry whether to wait for the service to start if it's not already started
* @throws NoSuchElementException when the service is unavailable
- * @hide
*/
- @SystemApi
public static native final IHwBinder getService(
String iface,
String serviceName,
@@ -112,9 +99,7 @@
* @param maxThreads total number of threads to create (includes this thread if
* callerWillJoin is true)
* @param callerWillJoin whether joinRpcThreadpool will be called in advance
- * @hide
*/
- @SystemApi
public static native final void configureRpcThreadpool(
long maxThreads, boolean callerWillJoin);
@@ -124,10 +109,7 @@
* a threadpool with callerWillJoin true and then registering
* the provided service if this thread doesn't need to do
* anything else.
- *
- * @hide
*/
- @SystemApi
public static native final void joinRpcThreadpool();
// Returns address of the "freeFunction".
@@ -155,10 +137,7 @@
* - tries to enable atracing (if enabled)
* - tries to enable coverage dumps (if running in VTS)
* - tries to enable record and replay (if running in VTS)
- *
- * @hide
*/
- @SystemApi
public static void enableInstrumentation() {
native_report_sysprop_change();
}
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index fbdf27e..249eb3a 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -28,10 +28,7 @@
* @param request parceled transaction
* @param reply object to parcel reply into
* @param flags transaction flags to be chosen by wire protocol
- *
- * @hide
*/
- @SystemApi
public void transact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
@@ -40,23 +37,19 @@
* Return as IHwInterface instance only if this implements descriptor.
*
* @param descriptor for example foo.bar@1.0::IBaz
- * @hide
*/
- @SystemApi
public IHwInterface queryLocalInterface(String descriptor);
/**
* Interface for receiving a callback when the process hosting a service
* has gone away.
*/
- @SystemApi
public interface DeathRecipient {
/**
* Callback for a registered process dying.
*
* @param cookie cookie this death recipient was registered with.
*/
- @SystemApi
public void serviceDied(long cookie);
}
@@ -67,13 +60,11 @@
* @param recipient callback object to be called on object death.
* @param cookie value to be given to callback on object death.
*/
- @SystemApi
public boolean linkToDeath(DeathRecipient recipient, long cookie);
/**
* Unregisters the death recipient from this binder.
*
* @param recipient callback to no longer recieve death notifications on this binder.
*/
- @SystemApi
public boolean unlinkToDeath(DeathRecipient recipient);
}
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index 1d9e2b0..f9edd5b 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -23,6 +23,5 @@
/**
* @return the binder object that corresponds to this interface.
*/
- @SystemApi
public IHwBinder asBinder();
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c9c4205..be8cf0e 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -18,7 +18,6 @@
package android.os;
import android.net.InterfaceConfiguration;
-import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.ITetheringStatsProvider;
import android.net.Network;
@@ -47,11 +46,6 @@
void unregisterObserver(INetworkManagementEventObserver obs);
/**
- * Retrieve an INetd to talk to netd.
- */
- INetd getNetdService();
-
- /**
* Returns a list of currently known network interfaces
*/
String[] listInterfaces();
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8160338..9280cb9 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -67,7 +67,7 @@
/**
* Register a listener for thermal status change.
- * @param listener the IThermalStatusListener to be notified.
+ * @param listener the {@link android.os.IThermalStatusListener} to be notified.
* @return true if registered successfully.
* {@hide}
*/
@@ -75,7 +75,7 @@
/**
* Unregister a previously-registered listener for thermal status.
- * @param listener the IThermalStatusListener to no longer be notified.
+ * @param listener the {@link android.os.IThermalStatusListener} to no longer be notified.
* @return true if unregistered successfully.
* {@hide}
*/
@@ -86,5 +86,5 @@
* @return status defined in {@link android.os.Temperature}.
* {@hide}
*/
- int getCurrentStatus();
+ int getCurrentThermalStatus();
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 894015f..c3e0489 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -17,7 +17,9 @@
package android.os;
import android.Manifest.permission;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -25,11 +27,15 @@
import android.annotation.TestApi;
import android.content.Context;
import android.service.dreams.Sandman;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
/**
* This class gives you control of the power state of the device.
@@ -643,6 +649,9 @@
final IPowerManager mService;
final Handler mHandler;
+ IThermalService mThermalService;
+ private ArrayMap<ThermalStatusCallback, IThermalStatusListener> mCallbackMap = new ArrayMap<>();
+
IDeviceIdleController mIDeviceIdleController;
/**
@@ -1443,6 +1452,159 @@
}
/**
+ * Thermal status code: Not under throttling.
+ */
+ public static final int THERMAL_STATUS_NONE = Temperature.THROTTLING_NONE;
+
+ /**
+ * Thermal status code: Light throttling where UX is not impacted.
+ */
+ public static final int THERMAL_STATUS_LIGHT = Temperature.THROTTLING_LIGHT;
+
+ /**
+ * Thermal status code: Moderate throttling where UX is not largely impacted.
+ */
+ public static final int THERMAL_STATUS_MODERATE = Temperature.THROTTLING_MODERATE;
+
+ /**
+ * Thermal status code: Severe throttling where UX is largely impacted.
+ */
+ public static final int THERMAL_STATUS_SEVERE = Temperature.THROTTLING_SEVERE;
+
+ /**
+ * Thermal status code: Platform has done everything to reduce power.
+ */
+ public static final int THERMAL_STATUS_CRITICAL = Temperature.THROTTLING_CRITICAL;
+
+ /**
+ * Thermal status code: Key components in platform are shutting down due to thermal condition.
+ * Device functionalities will be limited.
+ */
+ public static final int THERMAL_STATUS_EMERGENCY = Temperature.THROTTLING_EMERGENCY;
+
+ /**
+ * Thermal status code: Need shutdown immediately.
+ */
+ public static final int THERMAL_STATUS_SHUTDOWN = Temperature.THROTTLING_SHUTDOWN;
+
+ /** @hide */
+ @IntDef(prefix = { "THERMAL_STATUS_" }, value = {
+ THERMAL_STATUS_NONE,
+ THERMAL_STATUS_LIGHT,
+ THERMAL_STATUS_MODERATE,
+ THERMAL_STATUS_SEVERE,
+ THERMAL_STATUS_CRITICAL,
+ THERMAL_STATUS_EMERGENCY,
+ THERMAL_STATUS_SHUTDOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ThermalStatus {}
+
+ /**
+ * This function returns the current thermal status of the device.
+ *
+ * @return thermal status as int, {@link #THERMAL_STATUS_NONE} if device in not under
+ * thermal throttling.
+ */
+ public @ThermalStatus int getCurrentThermalStatus() {
+ synchronized (this) {
+ if (mThermalService == null) {
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+ try {
+ return mThermalService.getCurrentThermalStatus();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ }
+
+ /**
+ * Callback passed to
+ * {@link PowerManager#registerThermalStatusCallback} and
+ * {@link PowerManager#unregisterThermalStatusCallback}
+ * to notify caller of thermal status.
+ */
+ public abstract static class ThermalStatusCallback {
+
+ /**
+ * Called when overall thermal throttling status changed.
+ * @param status defined in {@link android.os.Temperature}.
+ */
+ public void onStatusChange(@ThermalStatus int status) {}
+ }
+
+ /**
+ * This function registers a callback for thermal status change.
+ *
+ * @param callback callback to be registered.
+ * @param executor {@link Executor} to handle the callbacks.
+ */
+ public void registerThermalStatusCallback(
+ @NonNull ThermalStatusCallback callback, @NonNull @CallbackExecutor Executor executor) {
+ Preconditions.checkNotNull(callback, "callback cannnot be null");
+ Preconditions.checkNotNull(executor, "executor cannnot be null");
+ synchronized (this) {
+ if (mThermalService == null) {
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+ try {
+ if (mCallbackMap.containsKey(callback)) {
+ throw new IllegalArgumentException("ThermalStatusCallback already registered");
+ }
+ IThermalStatusListener listener = new IThermalStatusListener.Stub() {
+ @Override
+ public void onStatusChange(int status) {
+ executor.execute(() -> {
+ callback.onStatusChange(status);
+ });
+ }
+ };
+ if (mThermalService.registerThermalStatusListener(listener)) {
+ mCallbackMap.put(callback, listener);
+ } else {
+ throw new RuntimeException("ThermalStatusCallback failed to register");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * This function unregisters a callback for thermal status change.
+ *
+ * @param callback to be unregistered.
+ *
+ * see {@link #registerThermalStatusCallback}
+ */
+ public void unregisterThermalStatusCallback(ThermalStatusCallback callback) {
+ Preconditions.checkNotNull(callback, "callback cannnot be null");
+ synchronized (this) {
+ if (mThermalService == null) {
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+ try {
+ IThermalStatusListener listener = mCallbackMap.get(callback);
+ if (listener == null) {
+ throw new IllegalArgumentException("ThermalStatusCallback not registered");
+ }
+ if (mThermalService.unregisterThermalStatusListener(listener)) {
+ mCallbackMap.remove(callback);
+ } else {
+ throw new RuntimeException("ThermalStatusCallback failed to unregister");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* If true, the doze component is not started until after the screen has been
* turned off and the screen off animation has been performed.
* @hide
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 2cb5aee..d55489a 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -200,4 +200,7 @@
* PowerHint defined in android/hardware/power/<version 1.0 & up>/IPower.h
*/
public abstract void powerHint(int hintId, int data);
+
+ /** Returns whether there hasn't been a user activity event for the given number of ms. */
+ public abstract boolean wasDeviceIdleFor(long ms);
}
diff --git a/core/java/android/os/SystemUpdateManager.java b/core/java/android/os/SystemUpdateManager.java
index ce3e2259..9146731 100644
--- a/core/java/android/os/SystemUpdateManager.java
+++ b/core/java/android/os/SystemUpdateManager.java
@@ -34,62 +34,51 @@
private static final String TAG = "SystemUpdateManager";
/** The status key of the system update info, expecting an int value. */
- @SystemApi
public static final String KEY_STATUS = "status";
/** The title of the current update, expecting a String value. */
- @SystemApi
public static final String KEY_TITLE = "title";
/** Whether it is a security update, expecting a boolean value. */
- @SystemApi
public static final String KEY_IS_SECURITY_UPDATE = "is_security_update";
/** The build fingerprint after installing the current update, expecting a String value. */
- @SystemApi
public static final String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
/** The security patch level after installing the current update, expecting a String value. */
- @SystemApi
public static final String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
/**
* The KEY_STATUS value that indicates there's no update status info available.
*/
- @SystemApi
public static final int STATUS_UNKNOWN = 0;
/**
* The KEY_STATUS value that indicates there's no pending update.
*/
- @SystemApi
public static final int STATUS_IDLE = 1;
/**
* The KEY_STATUS value that indicates an update is available for download, but pending user
* approval to start.
*/
- @SystemApi
public static final int STATUS_WAITING_DOWNLOAD = 2;
/**
* The KEY_STATUS value that indicates an update is in progress (i.e. downloading or installing
* has started).
*/
- @SystemApi
public static final int STATUS_IN_PROGRESS = 3;
/**
* The KEY_STATUS value that indicates an update is available for install.
*/
- @SystemApi
public static final int STATUS_WAITING_INSTALL = 4;
/**
* The KEY_STATUS value that indicates an update will be installed after a reboot. This applies
* to both of A/B and non-A/B OTAs.
*/
- @SystemApi
public static final int STATUS_WAITING_REBOOT = 5;
private final ISystemUpdateManager mService;
@@ -110,7 +99,6 @@
*
* @throws SecurityException if the caller is not allowed to read the info.
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_SYSTEM_UPDATE_INFO,
android.Manifest.permission.RECOVERY,
@@ -137,7 +125,6 @@
* @throws IllegalArgumentException if @link #KEY_STATUS} does not exist.
* @throws SecurityException if the caller is not allowed to update the info.
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.RECOVERY)
public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
if (infoBundle == null || !infoBundle.containsKey(KEY_STATUS)) {
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index bf85fbd..eee2b52 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -28,7 +28,7 @@
*
* @hide
*/
-public class Temperature implements Parcelable {
+public final class Temperature implements Parcelable {
/** Temperature value */
private float mValue;
/** A temperature type from ThermalHAL */
@@ -44,7 +44,7 @@
THROTTLING_MODERATE,
THROTTLING_SEVERE,
THROTTLING_CRITICAL,
- THROTTLING_WARNING,
+ THROTTLING_EMERGENCY,
THROTTLING_SHUTDOWN,
})
@Retention(RetentionPolicy.SOURCE)
@@ -56,7 +56,7 @@
public static final int THROTTLING_MODERATE = ThrottlingSeverity.MODERATE;
public static final int THROTTLING_SEVERE = ThrottlingSeverity.SEVERE;
public static final int THROTTLING_CRITICAL = ThrottlingSeverity.CRITICAL;
- public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
+ public static final int THROTTLING_EMERGENCY = ThrottlingSeverity.EMERGENCY;
public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
@IntDef(prefix = { "TYPE_" }, value = {
@@ -70,6 +70,7 @@
TYPE_BCL_VOLTAGE,
TYPE_BCL_CURRENT,
TYPE_BCL_PERCENTAGE,
+ TYPE_NPU,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
@@ -85,6 +86,7 @@
public static final int TYPE_BCL_VOLTAGE = TemperatureType.BCL_VOLTAGE;
public static final int TYPE_BCL_CURRENT = TemperatureType.BCL_CURRENT;
public static final int TYPE_BCL_PERCENTAGE = TemperatureType.BCL_PERCENTAGE;
+ public static final int TYPE_NPU = TemperatureType.NPU;
/**
* Verify a valid temperature type.
@@ -92,7 +94,7 @@
* @return true if a temperature type is valid otherwise false.
*/
public static boolean isValidType(@Type int type) {
- return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
+ return type >= TYPE_UNKNOWN && type <= TYPE_NPU;
}
/**
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index a967b3d..6ea155f 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -109,7 +109,7 @@
private static native void nativeSetTracingEnabled(boolean allowed);
@FastNative
- private static native void nativeTraceCounter(long tag, String name, int value);
+ private static native void nativeTraceCounter(long tag, String name, long value);
@FastNative
private static native void nativeTraceBegin(long tag, String name);
@FastNative
@@ -365,7 +365,7 @@
* @param counterName The counter name to appear in the trace.
* @param counterValue The counter value.
*/
- public static void setCounter(String counterName, int counterValue) {
+ public static void setCounter(String counterName, long counterValue) {
if (isTagEnabled(TRACE_TAG_APP)) {
nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
}
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 24c9c91..8f2826c 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -54,7 +54,6 @@
* Error code from the update engine. Values must agree with the ones in
* system/update_engine/common/error_code.h.
*/
- @SystemApi
public static final class ErrorCodeConstants {
public static final int SUCCESS = 0;
public static final int ERROR = 1;
@@ -74,7 +73,6 @@
* Update status code from the update engine. Values must agree with the
* ones in system/update_engine/client_library/include/update_engine/update_status.h.
*/
- @SystemApi
public static final class UpdateStatusConstants {
public static final int IDLE = 0;
public static final int CHECKING_FOR_UPDATE = 1;
@@ -95,7 +93,6 @@
/**
* Creates a new instance.
*/
- @SystemApi
public UpdateEngine() {
mUpdateEngine = IUpdateEngine.Stub.asInterface(
ServiceManager.getService(UPDATE_ENGINE_SERVICE));
@@ -106,7 +103,6 @@
* status change, and when the update completes. A handler can be supplied
* to control which thread runs the callback, or null.
*/
- @SystemApi
public boolean bind(final UpdateEngineCallback callback, final Handler handler) {
synchronized (mUpdateEngineCallbackLock) {
mUpdateEngineCallback = new IUpdateEngineCallback.Stub() {
@@ -150,7 +146,6 @@
/**
* Equivalent to {@code bind(callback, null)}.
*/
- @SystemApi
public boolean bind(final UpdateEngineCallback callback) {
return bind(callback, null);
}
@@ -183,7 +178,6 @@
* };
* </pre>
*/
- @SystemApi
public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) {
try {
mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
@@ -201,7 +195,6 @@
* <p>See {@link #suspend} for a way to temporarily stop an in-progress
* update with the ability to resume it later.
*/
- @SystemApi
public void cancel() {
try {
mUpdateEngine.cancel();
@@ -214,7 +207,6 @@
* Suspends an in-progress update. This can be undone by calling
* {@link #resume}.
*/
- @SystemApi
public void suspend() {
try {
mUpdateEngine.suspend();
@@ -226,7 +218,6 @@
/**
* Resumes a suspended update.
*/
- @SystemApi
public void resume() {
try {
mUpdateEngine.resume();
@@ -244,7 +235,6 @@
* {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding
* notification that rebooting into the new system is possible.
*/
- @SystemApi
public void resetStatus() {
try {
mUpdateEngine.resetStatus();
@@ -256,7 +246,6 @@
/**
* Unbinds the last bound callback function.
*/
- @SystemApi
public boolean unbind() {
synchronized (mUpdateEngineCallbackLock) {
if (mUpdateEngineCallback == null) {
@@ -281,7 +270,6 @@
* @param payloadMetadataFilename the location of the metadata without the
* {@code file://} prefix.
*/
- @SystemApi
public boolean verifyPayloadMetadata(String payloadMetadataFilename) {
try {
return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename);
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
index afff60a..f07294e 100644
--- a/core/java/android/os/UpdateEngineCallback.java
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -37,7 +37,6 @@
* be one of the values from {@link UpdateEngine.UpdateStatusConstants},
* and {@code percent} will be valid [TODO: in which cases?].
*/
- @SystemApi
public abstract void onStatusUpdate(int status, float percent);
/**
@@ -45,6 +44,5 @@
* unsuccessfully. The value of {@code errorCode} will be one of the
* values from {@link UpdateEngine.ErrorCodeConstants}.
*/
- @SystemApi
public abstract void onPayloadApplicationComplete(int errorCode);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index abfcfaa..17ce79b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -948,7 +948,7 @@
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- public static final String DISALLOW_INTELLIGENCE_CAPTURE = "no_intelligence_capture";
+ public static final String DISALLOW_CONTENT_CAPTURE = "no_content_capture";
/**
* Specifies if user switching is blocked on the current user.
@@ -1225,6 +1225,9 @@
* system user hasn't been unlocked yet, or {@link #DISALLOW_USER_SWITCH} is set.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean canSwitchUsers() {
boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt(
mContext.getContentResolver(),
@@ -2646,6 +2649,19 @@
}
/**
+ * Removes a user and all associated data.
+ *
+ * @param user the user that needs to be removed.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean removeUser(UserHandle user) {
+ return removeUser(user.getIdentifier());
+ }
+
+
+ /**
* Similar to {@link #removeUser(int)} except bypassing the checking of
* {@link UserManager#DISALLOW_REMOVE_USER}
* or {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE}.
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bf988ae..f114b12 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -190,4 +190,5 @@
void abortIdleMaintenance() = 80;
String translateAppToSystem(String path, int pid, int uid) = 81;
String translateSystemToApp(String path, int pid, int uid) = 82;
+ void commitChanges() = 83;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8e11d85..d315383 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -56,6 +56,7 @@
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.sysprop.VoldProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -1495,7 +1496,7 @@
* framework, so no service needs to check for changes during their lifespan
*/
public static boolean isBlockEncrypting() {
- final String state = SystemProperties.get("vold.encrypt_progress", "");
+ final String state = VoldProperties.encrypt_progress().orElse("");
return !"".equalsIgnoreCase(state);
}
@@ -1511,7 +1512,7 @@
* framework, so no service needs to check for changes during their lifespan
*/
public static boolean inCryptKeeperBounce() {
- final String status = SystemProperties.get("vold.decrypt");
+ final String status = VoldProperties.decrypt().orElse("");
return "trigger_restart_min_framework".equals(status);
}
diff --git a/core/java/android/permission/IRuntimePermissionPresenter.aidl b/core/java/android/permission/IRuntimePermissionPresenter.aidl
new file mode 100644
index 0000000..e95428a
--- /dev/null
+++ b/core/java/android/permission/IRuntimePermissionPresenter.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.os.RemoteCallback;
+
+/**
+ * Interface for communication with the permission presenter service.
+ *
+ * @hide
+ */
+oneway interface IRuntimePermissionPresenter {
+ void getAppPermissions(String packageName, in RemoteCallback callback);
+ void revokeRuntimePermission(String packageName, String permissionName);
+ void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
+ boolean countSystem, in RemoteCallback callback);
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl b/core/java/android/permission/RuntimePermissionPresentationInfo.aidl
similarity index 94%
rename from core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl
rename to core/java/android/permission/RuntimePermissionPresentationInfo.aidl
index f96a32f..533c1ff 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.content.pm.permission;
+package android.permission;
parcelable RuntimePermissionPresentationInfo;
diff --git a/core/java/android/permission/RuntimePermissionPresentationInfo.java b/core/java/android/permission/RuntimePermissionPresentationInfo.java
new file mode 100644
index 0000000..ed7b05c
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is to be presented in the UI. A single runtime permission
+ * presented to the user may correspond to multiple platform defined
+ * permissions, e.g. the location permission may control both the
+ * coarse and fine platform permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionPresentationInfo implements Parcelable {
+ private static final int FLAG_GRANTED = 1 << 0;
+ private static final int FLAG_STANDARD = 1 << 1;
+
+ private final CharSequence mLabel;
+ private final int mFlags;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param label The permission label.
+ * @param granted Whether the permission is granted.
+ * @param standard Whether this is a platform-defined permission.
+ */
+ public RuntimePermissionPresentationInfo(CharSequence label,
+ boolean granted, boolean standard) {
+ mLabel = label;
+ int flags = 0;
+ if (granted) {
+ flags |= FLAG_GRANTED;
+ }
+ if (standard) {
+ flags |= FLAG_STANDARD;
+ }
+ mFlags = flags;
+ }
+
+ private RuntimePermissionPresentationInfo(Parcel parcel) {
+ mLabel = parcel.readCharSequence();
+ mFlags = parcel.readInt();
+ }
+
+ /**
+ * @return Whether the permission is granted.
+ */
+ public boolean isGranted() {
+ return (mFlags & FLAG_GRANTED) != 0;
+ }
+
+ /**
+ * @return Whether the permission is platform-defined.
+ */
+ public boolean isStandard() {
+ return (mFlags & FLAG_STANDARD) != 0;
+ }
+
+ /**
+ * Gets the permission label.
+ *
+ * @return The label.
+ */
+ public @NonNull CharSequence getLabel() {
+ return mLabel;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeCharSequence(mLabel);
+ parcel.writeInt(mFlags);
+ }
+
+ public static final Creator<RuntimePermissionPresentationInfo> CREATOR =
+ new Creator<RuntimePermissionPresentationInfo>() {
+ public RuntimePermissionPresentationInfo createFromParcel(Parcel source) {
+ return new RuntimePermissionPresentationInfo(source);
+ }
+
+ public RuntimePermissionPresentationInfo[] newArray(int size) {
+ return new RuntimePermissionPresentationInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/permission/RuntimePermissionPresenter.java b/core/java/android/permission/RuntimePermissionPresenter.java
new file mode 100644
index 0000000..3ce3c9d
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionPresenter.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class provides information about runtime permissions for a specific
+ * app or all apps. This information is dedicated for presentation purposes
+ * and does not necessarily reflect the individual permissions requested/
+ * granted to an app as the platform may be grouping permissions to improve
+ * presentation and help the user make an informed choice. For example, all
+ * runtime permissions in the same permission group may be presented as a
+ * single permission in the UI.
+ *
+ * @hide
+ */
+public final class RuntimePermissionPresenter {
+ private static final String TAG = "RuntimePermPresenter";
+
+ /**
+ * The key for retrieving the result from the returned bundle.
+ *
+ * @hide
+ */
+ public static final String KEY_RESULT =
+ "android.permission.RuntimePermissionPresenter.key.result";
+
+ /**
+ * Listener for delivering the result of {@link #getAppPermissions}.
+ */
+ public interface OnGetAppPermissionResultCallback {
+ /**
+ * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback,
+ * Handler)}.
+ *
+ * @param permissions The permissions list.
+ */
+ void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions);
+ }
+
+ /**
+ * Listener for delivering the result of {@link #countPermissionApps}.
+ */
+ public interface OnCountPermissionAppsResultCallback {
+ /**
+ * The result for {@link #countPermissionApps(List, boolean,
+ * OnCountPermissionAppsResultCallback, Handler)}.
+ *
+ * @param numApps The number of apps that have one of the permissions
+ */
+ void onCountPermissionApps(int numApps);
+ }
+
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static RuntimePermissionPresenter sInstance;
+
+ private final RemoteService mRemoteService;
+
+ /**
+ * Gets the singleton runtime permission presenter.
+ *
+ * @param context Context for accessing resources.
+ * @return The singleton instance.
+ */
+ public static RuntimePermissionPresenter getInstance(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new RuntimePermissionPresenter(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+ }
+
+ private RuntimePermissionPresenter(Context context) {
+ mRemoteService = new RemoteService(context);
+ }
+
+ /**
+ * Gets the runtime permissions for an app.
+ *
+ * @param packageName The package for which to query.
+ * @param callback Callback to receive the result.
+ * @param handler Handler on which to invoke the callback.
+ */
+ public void getAppPermissions(@NonNull String packageName,
+ @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
+ checkNotNull(packageName);
+ checkNotNull(callback);
+
+ mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
+ mRemoteService, packageName, callback, handler));
+ }
+
+ /**
+ * Revoke the permission {@code permissionName} for app {@code packageName}
+ *
+ * @param packageName The package for which to revoke
+ * @param permissionName The permission to revoke
+ */
+ public void revokeRuntimePermission(@NonNull String packageName,
+ @NonNull String permissionName) {
+ checkNotNull(packageName);
+ checkNotNull(permissionName);
+
+ mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
+ mRemoteService, packageName, permissionName));
+ }
+
+ /**
+ * Count how many apps have one of a set of permissions.
+ *
+ * @param permissionNames The permissions the app might have
+ * @param countOnlyGranted Count an app only if the permission is granted to the app
+ * @param countSystem Also count system apps
+ * @param callback Callback to receive the result
+ * @param handler Handler on which to invoke the callback
+ */
+ public void countPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem,
+ @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
+ checkCollectionElementsNotNull(permissionNames, "permissionNames");
+ checkNotNull(callback);
+
+ mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
+ mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
+ }
+
+ private static final class RemoteService
+ extends Handler implements ServiceConnection {
+ private static final long UNBIND_TIMEOUT_MILLIS = 10000;
+
+ public static final int MSG_UNBIND = 0;
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+
+ @GuardedBy("mLock")
+ private final List<Message> mPendingWork = new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private IRuntimePermissionPresenter mRemoteInstance;
+
+ @GuardedBy("mLock")
+ private boolean mBound;
+
+ RemoteService(Context context) {
+ super(context.getMainLooper(), null, false);
+ mContext = context;
+ }
+
+ public void processMessage(Message message) {
+ synchronized (mLock) {
+ if (!mBound) {
+ Intent intent = new Intent(
+ RuntimePermissionPresenterService.SERVICE_INTERFACE);
+ intent.setPackage(mContext.getPackageManager()
+ .getPermissionControllerPackageName());
+ mBound = mContext.bindService(intent, this,
+ Context.BIND_AUTO_CREATE);
+ }
+ mPendingWork.add(message);
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ mRemoteInstance = null;
+ }
+ }
+
+ private void getAppPermissions(@NonNull String packageName,
+ @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
+ final IRuntimePermissionPresenter remoteInstance;
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+ try {
+ remoteInstance.getAppPermissions(packageName,
+ new RemoteCallback(result -> {
+ final List<RuntimePermissionPresentationInfo> reportedPermissions;
+ List<RuntimePermissionPresentationInfo> permissions = null;
+ if (result != null) {
+ permissions = result.getParcelableArrayList(KEY_RESULT);
+ }
+ if (permissions == null) {
+ permissions = Collections.emptyList();
+ }
+ reportedPermissions = permissions;
+ if (handler != null) {
+ handler.post(
+ () -> callback.onGetAppPermissions(reportedPermissions));
+ } else {
+ callback.onGetAppPermissions(reportedPermissions);
+ }
+ }, this));
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting app permissions", re);
+ }
+ scheduleUnbind();
+
+ synchronized (mLock) {
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ private void revokeAppPermissions(@NonNull String packageName,
+ @NonNull String permissionName) {
+ final IRuntimePermissionPresenter remoteInstance;
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+ try {
+ remoteInstance.revokeRuntimePermission(packageName, permissionName);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting app permissions", re);
+ }
+
+ synchronized (mLock) {
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ private void countPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem,
+ @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
+ final IRuntimePermissionPresenter remoteInstance;
+
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+
+ try {
+ remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
+ new RemoteCallback(result -> {
+ final int numApps;
+ if (result != null) {
+ numApps = result.getInt(KEY_RESULT);
+ } else {
+ numApps = 0;
+ }
+
+ if (handler != null) {
+ handler.post(() -> callback.onCountPermissionApps(numApps));
+ } else {
+ callback.onCountPermissionApps(numApps);
+ }
+ }, this));
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error counting permission apps", re);
+ }
+
+ scheduleUnbind();
+
+ synchronized (mLock) {
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ private void unbind() {
+ synchronized (mLock) {
+ if (mBound) {
+ mContext.unbindService(this);
+ mBound = false;
+ }
+ mRemoteInstance = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleNextMessageIfNeededLocked() {
+ if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
+ Message nextMessage = mPendingWork.remove(0);
+ sendMessage(nextMessage);
+ }
+ }
+
+ private void scheduleUnbind() {
+ removeMessages(MSG_UNBIND);
+ sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
+ .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
+ }
+ }
+}
diff --git a/core/java/android/permission/RuntimePermissionPresenterService.java b/core/java/android/permission/RuntimePermissionPresenterService.java
new file mode 100644
index 0000000..81ec7be
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionPresenterService.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+
+import java.util.List;
+
+/**
+ * This service presents information regarding runtime permissions that is
+ * used for presenting them in the UI. Runtime permissions are presented as
+ * a single permission in the UI but may be composed of several individual
+ * permissions.
+ *
+ * @see RuntimePermissionPresenter
+ * @see RuntimePermissionPresentationInfo
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class RuntimePermissionPresenterService extends Service {
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service
+ * in its manifest for the system to recognize it as a runtime permission
+ * presenter service.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.permission.RuntimePermissionPresenterService";
+
+ // No need for locking - always set first and never modified
+ private Handler mHandler;
+
+ @Override
+ public final void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ mHandler = new Handler(base.getMainLooper());
+ }
+
+ /**
+ * Gets the runtime permissions for an app.
+ *
+ * @param packageName The package for which to query.
+ *
+ * @return descriptions of the runtime permissions of the app
+ */
+ public abstract @NonNull List<RuntimePermissionPresentationInfo> onGetAppPermissions(
+ @NonNull String packageName);
+
+ /**
+ * Revokes the permission {@code permissionName} for app {@code packageName}
+ *
+ * @param packageName The package for which to revoke
+ * @param permissionName The permission to revoke
+ */
+ public abstract void onRevokeRuntimePermission(@NonNull String packageName,
+ @NonNull String permissionName);
+
+ /**
+ * Count how many apps have one of a set of permissions.
+ *
+ * @param permissionNames The permissions the app might have
+ * @param countOnlyGranted Count an app only if the permission is granted to the app
+ * @param countSystem Also count system apps
+ *
+ * @return the number of apps that have one of the permissions
+ */
+ public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem);
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IRuntimePermissionPresenter.Stub() {
+ @Override
+ public void getAppPermissions(String packageName, RemoteCallback callback) {
+ checkNotNull(packageName, "packageName");
+ checkNotNull(callback, "callback");
+
+ mHandler.sendMessage(
+ obtainMessage(
+ RuntimePermissionPresenterService::getAppPermissions,
+ RuntimePermissionPresenterService.this, packageName, callback));
+ }
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String permissionName) {
+ checkNotNull(packageName, "packageName");
+ checkNotNull(permissionName, "permissionName");
+
+ mHandler.sendMessage(
+ obtainMessage(
+ RuntimePermissionPresenterService::onRevokeRuntimePermission,
+ RuntimePermissionPresenterService.this, packageName,
+ permissionName));
+ }
+
+ @Override
+ public void countPermissionApps(List<String> permissionNames, boolean countOnlyGranted,
+ boolean countSystem, RemoteCallback callback) {
+ checkCollectionElementsNotNull(permissionNames, "permissionNames");
+ checkNotNull(callback, "callback");
+
+ mHandler.sendMessage(
+ obtainMessage(
+ RuntimePermissionPresenterService::countPermissionApps,
+ RuntimePermissionPresenterService.this, permissionNames,
+ countOnlyGranted, countSystem, callback));
+ }
+ };
+ }
+
+ private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
+ List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
+ if (permissions != null && !permissions.isEmpty()) {
+ Bundle result = new Bundle();
+ result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
+ }
+ }
+
+ private void countPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem, @NonNull RemoteCallback callback) {
+ int numApps = onCountPermissionApps(permissionNames, countOnlyGranted, countSystem);
+
+ Bundle result = new Bundle();
+ result.putInt(RuntimePermissionPresenter.KEY_RESULT, numApps);
+ callback.sendResult(result);
+ }
+}
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 18aea03..2b3f0f5 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,6 +16,9 @@
package android.permissionpresenterservice;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
@@ -23,16 +26,11 @@
import android.content.Intent;
import android.content.pm.permission.IRuntimePermissionPresenter;
import android.content.pm.permission.RuntimePermissionPresentationInfo;
-import android.content.pm.permission.RuntimePermissionPresenter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteCallback;
-import com.android.internal.os.SomeArgs;
-
import java.util.List;
/**
@@ -41,11 +39,13 @@
* a single permission in the UI but may be composed of several individual
* permissions.
*
- * @see RuntimePermissionPresenter
* @see RuntimePermissionPresentationInfo
*
* @hide
+ *
+ * @deprecated use {@link android.permission.RuntimePermissionPresenterService} instead
*/
+@Deprecated
@SystemApi
public abstract class RuntimePermissionPresenterService extends Service {
@@ -57,13 +57,16 @@
public static final String SERVICE_INTERFACE =
"android.permissionpresenterservice.RuntimePermissionPresenterService";
+ private static final String KEY_RESULT =
+ "android.content.pm.permission.RuntimePermissionPresenter.key.result";
+
// No need for locking - always set first and never modified
private Handler mHandler;
@Override
public final void attachBaseContext(Context base) {
super.attachBaseContext(base);
- mHandler = new MyHandler(base.getMainLooper());
+ mHandler = new Handler(base.getMainLooper());
}
/**
@@ -71,7 +74,8 @@
*
* @param packageName The package for which to query.
*/
- public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
+ public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(
+ @NonNull String packageName);
/**
* Revokes the permission {@code permissionName} for app {@code packageName}
@@ -87,61 +91,35 @@
return new IRuntimePermissionPresenter.Stub() {
@Override
public void getAppPermissions(String packageName, RemoteCallback callback) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = callback;
- mHandler.obtainMessage(MyHandler.MSG_GET_APP_PERMISSIONS,
- args).sendToTarget();
+ checkNotNull(packageName, "packageName");
+ checkNotNull(callback, "callback");
+
+ mHandler.sendMessage(
+ obtainMessage(RuntimePermissionPresenterService::getAppPermissions,
+ RuntimePermissionPresenterService.this, packageName, callback));
}
@Override
public void revokeRuntimePermission(String packageName, String permissionName) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = permissionName;
- mHandler.obtainMessage(MyHandler.MSG_REVOKE_APP_PERMISSION,
- args).sendToTarget();
+ checkNotNull(packageName, "packageName");
+ checkNotNull(permissionName, "permissionName");
+
+ mHandler.sendMessage(
+ obtainMessage(RuntimePermissionPresenterService::onRevokeRuntimePermission,
+ RuntimePermissionPresenterService.this, packageName,
+ permissionName));
}
};
}
- private final class MyHandler extends Handler {
- public static final int MSG_GET_APP_PERMISSIONS = 1;
- public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
- public static final int MSG_REVOKE_APP_PERMISSION = 3;
-
- public MyHandler(Looper looper) {
- super(looper, null, false);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_GET_APP_PERMISSIONS: {
- SomeArgs args = (SomeArgs) msg.obj;
- String packageName = (String) args.arg1;
- RemoteCallback callback = (RemoteCallback) args.arg2;
- args.recycle();
- List<RuntimePermissionPresentationInfo> permissions =
- onGetAppPermissions(packageName);
- if (permissions != null && !permissions.isEmpty()) {
- Bundle result = new Bundle();
- result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT,
- permissions);
- callback.sendResult(result);
- } else {
- callback.sendResult(null);
- }
- } break;
- case MSG_REVOKE_APP_PERMISSION: {
- SomeArgs args = (SomeArgs) msg.obj;
- String packageName = (String) args.arg1;
- String permissionName = (String) args.arg2;
- args.recycle();
-
- onRevokeRuntimePermission(packageName, permissionName);
- } break;
- }
+ private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
+ List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
+ if (permissions != null && !permissions.isEmpty()) {
+ Bundle result = new Bundle();
+ result.putParcelableList(KEY_RESULT, permissions);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
}
}
}
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index a8f3665..c167ea1 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -16,13 +16,15 @@
package android.provider;
-
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -40,6 +42,8 @@
import android.text.format.Time;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
/**
* <p>
* The contract between the calendar provider and applications. Contains
@@ -128,6 +132,13 @@
"android.provider.calendar.action.HANDLE_CUSTOM_EVENT";
/**
+ * Action used to help apps show calendar events in the managed profile.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VIEW_WORK_CALENDAR_EVENT =
+ "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT";
+
+ /**
* Intent Extras key: {@link EventsColumns#CUSTOM_APP_URI} for the event in
* the {@link #ACTION_HANDLE_CUSTOM_EVENT} intent
*/
@@ -152,6 +163,11 @@
public static final String EXTRA_EVENT_ALL_DAY = "allDay";
/**
+ * Intent Extras key: The id of an event.
+ */
+ public static final String EXTRA_EVENT_ID = "id";
+
+ /**
* This authority is used for writing to or querying from the calendar
* provider. Note: This is set at first run and cannot be changed without
* breaking apps that access the provider.
@@ -194,6 +210,43 @@
private CalendarContract() {}
/**
+ * Starts an activity to view calendar events in the managed profile.
+ *
+ * When this API is called, the system will attempt to start an activity
+ * in the managed profile with an intent targeting the same caller package.
+ * The intent will have its action set to
+ * {@link CalendarContract#ACTION_VIEW_WORK_CALENDAR_EVENT} and contain extras
+ * corresponding to the API's arguments. A calendar app intending to support
+ * cross profile events viewing should handle this intent, parse the arguments
+ * and show the appropriate UI.
+ *
+ * @param context the context.
+ * @param eventId the id of the event to be viewed. Will be put into {@link #EXTRA_EVENT_ID}
+ * field of the intent.
+ * @param start the start time of the event. Will be put into {@link #EXTRA_EVENT_BEGIN_TIME}
+ * field of the intent.
+ * @param end the end time of the event. Will be put into {@link #EXTRA_EVENT_END_TIME} field
+ * of the intent.
+ * @param allDay if the event is an all-day event. Will be put into
+ * {@link #EXTRA_EVENT_ALL_DAY} field of the intent.
+ * @param flags flags to be set on the intent via {@link Intent#setFlags}
+ * @return {@code true} if the activity is started successfully. {@code false} otherwise.
+ *
+ * @see #EXTRA_EVENT_ID
+ * @see #EXTRA_EVENT_BEGIN_TIME
+ * @see #EXTRA_EVENT_END_TIME
+ * @see #EXTRA_EVENT_ALL_DAY
+ */
+ public static boolean startViewCalendarEventInManagedProfile(@NonNull Context context,
+ long eventId, long start, long end, boolean allDay, int flags) {
+ Preconditions.checkNotNull(context, "Context is null");
+ final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ return dpm.startViewCalendarEventInManagedProfile(eventId, start,
+ end, allDay, flags);
+ }
+
+ /**
* Generic columns for use by sync adapters. The specific functions of these
* columns are private to the sync adapter. Other clients of the API should
* not attempt to either read or write this column. These columns are
@@ -694,6 +747,37 @@
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
/**
+ * The content:// style URL for querying Calendars table in the managed profile. Appending a
+ * calendar id using {@link ContentUris#withAppendedId(Uri, long)} will
+ * specify a single calendar.
+ *
+ * <p>The following columns are allowed to be queried via this uri:
+ * <ul>
+ * <li>{@link #_ID}</li>
+ * <li>{@link #NAME}</li>
+ * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
+ * <li>{@link #CALENDAR_COLOR}</li>
+ * <li>{@link #VISIBLE}</li>
+ * <li>{@link #CALENDAR_LOCATION}</li>
+ * <li>{@link #CALENDAR_TIME_ZONE}</li>
+ * <li>{@link #IS_PRIMARY}</li>
+ * </ul>
+ *
+ * <p>{@link IllegalArgumentException} will be thrown if there exist columns in the
+ * projection of the query to this uri that are not contained in the above list.
+ *
+ * <p>This uri will return an empty cursor if the calling user is not a parent profile
+ * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is
+ * queried from a package that is not whitelisted by profile owner of the managed profile
+ * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ *
+ * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
+ * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
+ */
+ public static final Uri ENTERPRISE_CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/enterprise/calendars");
+
+ /**
* The default sort order for this table
*/
public static final String DEFAULT_SORT_ORDER = CALENDAR_DISPLAY_NAME;
@@ -1641,6 +1725,50 @@
Uri.parse("content://" + AUTHORITY + "/events");
/**
+ * The content:// style URL for querying Events table in the managed profile. Appending an
+ * event id using {@link ContentUris#withAppendedId(Uri, long)} will
+ * specify a single event.
+ *
+ * <p>The following columns are allowed to be queried via this uri:
+ * <ul>
+ * <li>{@link #_ID}</li>
+ * <li>{@link #CALENDAR_ID}</li>
+ * <li>{@link #TITLE}</li>
+ * <li>{@link #EVENT_LOCATION}</li>
+ * <li>{@link #EVENT_COLOR}</li>
+ * <li>{@link #STATUS}</li>
+ * <li>{@link #DTSTART}</li>
+ * <li>{@link #DTEND}</li>
+ * <li>{@link #EVENT_TIMEZONE}</li>
+ * <li>{@link #EVENT_END_TIMEZONE}</li>
+ * <li>{@link #DURATION}</li>
+ * <li>{@link #ALL_DAY}</li>
+ * <li>{@link #AVAILABILITY}</li>
+ * <li>{@link #RRULE}</li>
+ * <li>{@link #RDATE}</li>
+ * <li>{@link #EXRULE}</li>
+ * <li>{@link #EXDATE}</li>
+ * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
+ * <li>{@link #CALENDAR_COLOR}</li>
+ * <li>{@link #VISIBLE}</li>
+ * <li>{@link #CALENDAR_TIME_ZONE}</li>
+ * </ul>
+ *
+ * <p>{@link IllegalArgumentException} will be thrown if there exist columns in the
+ * projection of the query to this uri that are not contained in the above list.
+ *
+ * <p>This uri will return an empty cursor if the calling user is not a parent profile
+ * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is
+ * queried from a package that is not whitelisted by profile owner of the managed profile
+ * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ *
+ * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
+ * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
+ */
+ public static final Uri ENTERPRISE_CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/enterprise/events");
+
+ /**
* The content:// style URI for recurring event exceptions. Insertions require an
* appended event ID. Deletion of exceptions requires both the original event ID and
* the exception event ID (see {@link Uri.Builder#appendPath}).
@@ -1820,6 +1948,63 @@
Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
/**
+ * The content:// style URL for querying an instance range in the managed profile.
+ * It supports similar semantics as {@link #CONTENT_URI}.
+ *
+ * <p>The following columns plus the columns that are whitelisted by
+ * {@link Events#ENTERPRISE_CONTENT_URI} are allowed to be queried via this uri:
+ * <ul>
+ * <li>{@link #_ID}</li>
+ * <li>{@link #EVENT_ID}</li>
+ * <li>{@link #BEGIN}</li>
+ * <li>{@link #END}</li>
+ * <li>{@link #START_DAY}</li>
+ * <li>{@link #END_DAY}</li>
+ * <li>{@link #START_MINUTE}</li>
+ * <li>{@link #END_MINUTE}</li>
+ * </ul>
+ *
+ * <p>{@link IllegalArgumentException} will be thrown if there exist columns in the
+ * projection of the query to this uri that are not contained in the above list.
+ *
+ * <p>This uri will return an empty cursor if the calling user is not a parent profile
+ * of a managed profile, or cross profile calendar for the managed profile is disabled in
+ * Settings, or this uri is queried from a package that is not whitelisted by
+ * profile owner of the managed profile via
+ * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ *
+ * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
+ * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
+ */
+ public static final Uri ENTERPRISE_CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/enterprise/instances/when");
+
+ /**
+ * The content:// style URL for querying an instance range by Julian
+ * Day in the managed profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI}
+ * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
+ */
+ public static final Uri ENTERPRISE_CONTENT_BY_DAY_URI =
+ Uri.parse("content://" + AUTHORITY + "/enterprise/instances/whenbyday");
+
+ /**
+ * The content:// style URL for querying an instance range with a search
+ * term in the managed profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI}
+ * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
+ */
+ public static final Uri ENTERPRISE_CONTENT_SEARCH_URI =
+ Uri.parse("content://" + AUTHORITY + "/enterprise/instances/search");
+
+ /**
+ * The content:// style URL for querying an instance range with a search
+ * term in the managed profile. It supports similar semantics as
+ * {@link #CONTENT_SEARCH_BY_DAY_URI} and performs similar checks as
+ * {@link #ENTERPRISE_CONTENT_URI}.
+ */
+ public static final Uri ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI =
+ Uri.parse("content://" + AUTHORITY + "/enterprise/instances/searchbyday");
+
+ /**
* The default sort order for this table.
*/
private static final String DEFAULT_SORT_ORDER = "begin ASC";
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index e032c18..a8726e9 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -22,8 +22,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
-import android.content.ContentProviderClient;
+import android.content.ContentInterface;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -47,10 +48,10 @@
import android.os.Parcelable;
import android.os.ParcelableException;
import android.os.RemoteException;
-import android.os.storage.StorageVolume;
-import android.util.DataUnit;
import android.util.Log;
+import dalvik.system.VMRuntime;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -67,8 +68,7 @@
* All client apps must hold a valid URI permission grant to access documents,
* typically issued when a user makes a selection through
* {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT},
- * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, or
- * {@link StorageVolume#createAccessIntent(String) StorageVolume.createAccessIntent}.
+ * or {@link Intent#ACTION_OPEN_DOCUMENT_TREE}.
*
* @see DocumentsProvider
*/
@@ -97,8 +97,15 @@
@Deprecated
public static final String EXTRA_PACKAGE_NAME = Intent.EXTRA_PACKAGE_NAME;
- /** {@hide} */
- public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";
+ /**
+ * The value is decide whether to show advance mode or not.
+ * If the value is true, the local/device storage root must be
+ * visible in DocumentsUI.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_SHOW_ADVANCED = "android.provider.extra.SHOW_ADVANCED";
/** {@hide} */
public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";
@@ -111,7 +118,6 @@
*
* @see DocumentsProvider#querySearchDocuments(String, String[],
* Bundle)
- * {@hide}
*/
public static final String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
@@ -124,7 +130,6 @@
*
* @see DocumentsProvider#querySearchDocuments(String, String[],
* Bundle)
- * {@hide}
*/
public static final String QUERY_ARG_MIME_TYPES = "android:query-arg-mime-types";
@@ -134,7 +139,6 @@
*
* @see DocumentsProvider#querySearchDocuments(String, String[],
* Bundle)
- * {@hide}
*/
public static final String QUERY_ARG_FILE_SIZE_OVER = "android:query-arg-file-size-over";
@@ -146,12 +150,21 @@
* @see DocumentsProvider#querySearchDocuments(String, String[],
* Bundle)
* @see Document#COLUMN_LAST_MODIFIED
- * {@hide}
*/
public static final String QUERY_ARG_LAST_MODIFIED_AFTER =
"android:query-arg-last-modified-after";
/**
+ * Key for {@link DocumentsProvider} to decide whether the files that
+ * have been added to MediaStore should be excluded. If the value is
+ * true, exclude them. Otherwise, include them.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ */
+ public static final String QUERY_ARG_EXCLUDE_MEDIA = "android:query-arg-exclude-media";
+
+ /**
* Sets the desired initial location visible to user when file chooser is shown.
*
* <p>Applicable to {@link Intent} with actions:
@@ -205,18 +218,21 @@
public static final String
ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
- /** {@hide} */
+ /**
+ * The action to manage document in Downloads root in DocumentsUI.
+ * {@hide}
+ */
+ @SystemApi
public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
- /** {@hide} */
+ /**
+ * The action to launch the settings of this root.
+ * {@hide}
+ */
+ @SystemApi
public static final String
ACTION_DOCUMENT_ROOT_SETTINGS = "android.provider.action.DOCUMENT_ROOT_SETTINGS";
- /**
- * Buffer is large enough to rewind past any EXIF headers.
- */
- private static final int THUMBNAIL_BUFFER_SIZE = (int) DataUnit.KIBIBYTES.toBytes(128);
-
/** {@hide} */
public static final String EXTERNAL_STORAGE_PROVIDER_AUTHORITY =
"com.android.externalstorage.documents";
@@ -224,13 +240,38 @@
/** {@hide} */
public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
- /** {@hide} */
- public static final String METADATA_TYPES = "android:documentMetadataType";
+ /**
+ * Get string array identifies the type or types of metadata returned
+ * using DocumentsContract#getDocumentMetadata.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
+ public static final String METADATA_TYPES = "android:documentMetadataTypes";
- /** {@hide} */
+ /**
+ * Get Exif information using DocumentsContract#getDocumentMetadata.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
public static final String METADATA_EXIF = "android:documentExif";
/**
+ * Get total count of all documents currently stored under the given
+ * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
+ public static final String METADATA_TREE_COUNT = "android:metadataTreeCount";
+
+ /**
+ * Get total size of all documents currently stored under the given
+ * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
+ public static final String METADATA_TREE_SIZE = "android:metadataTreeSize";
+
+ /**
* Constants related to a document, including {@link Cursor} column names
* and flags.
* <p>
@@ -350,7 +391,7 @@
* Flag indicating that a document can be represented as a thumbnail.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#getDocumentThumbnail(ContentResolver, Uri,
+ * @see DocumentsContract#getDocumentThumbnail(ContentInterface, Uri,
* Point, CancellationSignal)
* @see DocumentsProvider#openDocumentThumbnail(String, Point,
* android.os.CancellationSignal)
@@ -376,7 +417,7 @@
* Flag indicating that a document is deletable.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
+ * @see DocumentsContract#deleteDocument(ContentInterface, Uri)
* @see DocumentsProvider#deleteDocument(String)
*/
public static final int FLAG_SUPPORTS_DELETE = 1 << 2;
@@ -414,8 +455,7 @@
* Flag indicating that a document can be renamed.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#renameDocument(ContentResolver, Uri,
- * String)
+ * @see DocumentsContract#renameDocument(ContentInterface, Uri, String)
* @see DocumentsProvider#renameDocument(String, String)
*/
public static final int FLAG_SUPPORTS_RENAME = 1 << 6;
@@ -425,7 +465,7 @@
* within the same document provider.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#copyDocument(ContentResolver, Uri, Uri)
+ * @see DocumentsContract#copyDocument(ContentInterface, Uri, Uri)
* @see DocumentsProvider#copyDocument(String, String)
*/
public static final int FLAG_SUPPORTS_COPY = 1 << 7;
@@ -435,7 +475,7 @@
* within the same document provider.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#moveDocument(ContentResolver, Uri, Uri, Uri)
+ * @see DocumentsContract#moveDocument(ContentInterface, Uri, Uri, Uri)
* @see DocumentsProvider#moveDocument(String, String, String)
*/
public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
@@ -459,7 +499,7 @@
* Flag indicating that a document can be removed from a parent.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#removeDocument(ContentResolver, Uri, Uri)
+ * @see DocumentsContract#removeDocument(ContentInterface, Uri, Uri)
* @see DocumentsProvider#removeDocument(String, String)
*/
public static final int FLAG_SUPPORTS_REMOVE = 1 << 10;
@@ -487,16 +527,17 @@
* if they represent a failed download.
*
* @see #COLUMN_FLAGS
- * @hide
*/
- public static final int FLAG_PARTIAL = 1 << 16;
+ public static final int FLAG_PARTIAL = 1 << 13;
/**
* Flag indicating that a document has available metadata that can be read
* using DocumentsContract#getDocumentMetadata
- * @hide
+ *
+ * @see #COLUMN_FLAGS
+ * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
*/
- public static final int FLAG_SUPPORTS_METADATA = 1 << 17;
+ public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
}
/**
@@ -652,7 +693,7 @@
* Flag indicating that this root can be ejected.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#ejectRoot(ContentResolver, Uri)
+ * @see DocumentsContract#ejectRoot(ContentInterface, Uri)
* @see DocumentsProvider#ejectRoot(String)
*/
public static final int FLAG_SUPPORTS_EJECT = 1 << 5;
@@ -668,44 +709,46 @@
* @see #COLUMN_FLAGS
* @see ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)
- * @hide
*/
- public static final int FLAG_EMPTY = 1 << 16;
+ public static final int FLAG_EMPTY = 1 << 6;
/**
* Flag indicating that this root should only be visible to advanced
* users.
*
* @see #COLUMN_FLAGS
- * @hide
+ * {@hide}
*/
- @UnsupportedAppUsage
- public static final int FLAG_ADVANCED = 1 << 17;
+ @SystemApi
+ public static final int FLAG_ADVANCED = 1 << 16;
/**
* Flag indicating that this root has settings.
*
* @see #COLUMN_FLAGS
* @see DocumentsContract#ACTION_DOCUMENT_ROOT_SETTINGS
- * @hide
+ * {@hide}
*/
- public static final int FLAG_HAS_SETTINGS = 1 << 18;
+ @SystemApi
+ public static final int FLAG_HAS_SETTINGS = 1 << 17;
/**
* Flag indicating that this root is on removable SD card storage.
*
* @see #COLUMN_FLAGS
- * @hide
+ * {@hide}
*/
- public static final int FLAG_REMOVABLE_SD = 1 << 19;
+ @SystemApi
+ public static final int FLAG_REMOVABLE_SD = 1 << 18;
/**
* Flag indicating that this root is on removable USB storage.
*
* @see #COLUMN_FLAGS
- * @hide
+ * {@hide}
*/
- public static final int FLAG_REMOVABLE_USB = 1 << 20;
+ @SystemApi
+ public static final int FLAG_REMOVABLE_USB = 1 << 19;
}
/**
@@ -773,10 +816,7 @@
/** {@hide} */
public static final String EXTRA_URI_PERMISSIONS = "uriPermissions";
- /**
- * @see #createWebLinkIntent(ContentResolver, Uri, Bundle)
- * {@hide}
- */
+ /** {@hide} */
public static final String EXTRA_OPTIONS = "options";
private static final String PATH_ROOT = "root";
@@ -1017,6 +1057,7 @@
/**
* Get the handled query arguments from the query bundle. The handled arguments are
+ * {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA},
* {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
* {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
* {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER} and
@@ -1032,6 +1073,11 @@
}
final ArrayList<String> args = new ArrayList<>();
+
+ if (queryArgs.keySet().contains(QUERY_ARG_EXCLUDE_MEDIA)) {
+ args.add(QUERY_ARG_EXCLUDE_MEDIA);
+ }
+
if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) {
args.add(QUERY_ARG_DISPLAY_NAME);
}
@@ -1073,8 +1119,6 @@
* Test if the given URI represents roots backed by {@link DocumentsProvider}.
*
* @see #buildRootsUri(String)
- *
- * {@hide}
*/
public static boolean isRootsUri(Context context, @Nullable Uri uri) {
return isRootUri(context, uri, 1 /* pathSize */);
@@ -1084,8 +1128,6 @@
* Test if the given URI represents specific root backed by {@link DocumentsProvider}.
*
* @see #buildRootUri(String, String)
- *
- * {@hide}
*/
public static boolean isRootUri(Context context, @Nullable Uri uri) {
return isRootUri(context, uri, 2 /* pathSize */);
@@ -1183,13 +1225,23 @@
return bundle.getString(QUERY_ARG_DISPLAY_NAME, "" /* defaultValue */);
}
- /** {@hide} */
- @UnsupportedAppUsage
+ /**
+ * Build URI that append the query parameter {@link PARAM_MANAGE} to
+ * enable the manage mode.
+ * @see DocumentsProvider#queryChildDocumentsForManage(String parentDocId, String[], String)
+ * {@hide}
+ */
+ @SystemApi
public static Uri setManageMode(Uri uri) {
return uri.buildUpon().appendQueryParameter(PARAM_MANAGE, "true").build();
}
- /** {@hide} */
+ /**
+ * Extract the manage mode from a URI built by
+ * {@link #setManageMode(Uri)}.
+ * {@hide}
+ */
+ @SystemApi
public static boolean isManageMode(Uri uri) {
return uri.getBooleanQueryParameter(PARAM_MANAGE, false);
}
@@ -1209,30 +1261,24 @@
* @see DocumentsProvider#openDocumentThumbnail(String, Point,
* android.os.CancellationSignal)
*/
- public static Bitmap getDocumentThumbnail(
- ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal)
- throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- documentUri.getAuthority());
+ public static Bitmap getDocumentThumbnail(ContentInterface content, Uri documentUri, Point size,
+ CancellationSignal signal) throws FileNotFoundException {
try {
- return getDocumentThumbnail(client, documentUri, size, signal);
+ return ContentResolver.loadThumbnail(content, documentUri, Point.convert(size), signal,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
} catch (Exception e) {
if (!(e instanceof OperationCanceledException)) {
Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
}
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- @UnsupportedAppUsage
- public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
- Point size, CancellationSignal signal) throws IOException {
- return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
- ImageDecoder.ALLOCATOR_SOFTWARE);
+ /** @removed */
+ public static Bitmap getDocumentThumbnail(ContentResolver content, Uri documentUri, Point size,
+ CancellationSignal signal) throws FileNotFoundException {
+ return getDocumentThumbnail((ContentInterface) content, documentUri, size, signal);
}
/**
@@ -1243,49 +1289,66 @@
* @param displayName name of new document
* @return newly created document, or {@code null} if failed
*/
- public static Uri createDocument(ContentResolver resolver, Uri parentDocumentUri,
+ public static Uri createDocument(ContentInterface content, Uri parentDocumentUri,
String mimeType, String displayName) throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- parentDocumentUri.getAuthority());
try {
- return createDocument(client, parentDocumentUri, mimeType, displayName);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
+ in.putString(Document.COLUMN_MIME_TYPE, mimeType);
+ in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
+
+ final Bundle out = content.call(parentDocumentUri.getAuthority(),
+ METHOD_CREATE_DOCUMENT, null, in);
+ return out.getParcelable(DocumentsContract.EXTRA_URI);
} catch (Exception e) {
Log.w(TAG, "Failed to create document", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- public static Uri createDocument(ContentProviderClient client, Uri parentDocumentUri,
- String mimeType, String displayName) throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
- in.putString(Document.COLUMN_MIME_TYPE, mimeType);
- in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
-
- final Bundle out = client.call(METHOD_CREATE_DOCUMENT, null, in);
- return out.getParcelable(DocumentsContract.EXTRA_URI);
+ /** @removed */
+ public static Uri createDocument(ContentResolver content, Uri parentDocumentUri,
+ String mimeType, String displayName) throws FileNotFoundException {
+ return createDocument((ContentInterface) content, parentDocumentUri, mimeType, displayName);
}
- /** {@hide} */
- public static boolean isChildDocument(ContentProviderClient client, Uri parentDocumentUri,
- Uri childDocumentUri) throws RemoteException {
+ /**
+ * Test if a document is descendant (child, grandchild, etc) from the given
+ * parent.
+ *
+ * @param parentDocumentUri parent to verify against.
+ * @param childDocumentUri child to verify.
+ * @return if given document is a descendant of the given parent.
+ * @see Root#FLAG_SUPPORTS_IS_CHILD
+ */
+ public static boolean isChildDocument(ContentInterface content, Uri parentDocumentUri,
+ Uri childDocumentUri) throws FileNotFoundException {
+ try {
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
+ in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, childDocumentUri);
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
- in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, childDocumentUri);
+ final Bundle out = content.call(parentDocumentUri.getAuthority(),
+ METHOD_IS_CHILD_DOCUMENT, null, in);
+ if (out == null) {
+ throw new RemoteException("Failed to get a reponse from isChildDocument query.");
+ }
+ if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
+ throw new RemoteException("Response did not include result field..");
+ }
+ return out.getBoolean(DocumentsContract.EXTRA_RESULT);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to create document", e);
+ rethrowIfNecessary(e);
+ return false;
+ }
+ }
- final Bundle out = client.call(METHOD_IS_CHILD_DOCUMENT, null, in);
- if (out == null) {
- throw new RemoteException("Failed to get a reponse from isChildDocument query.");
- }
- if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
- throw new RemoteException("Response did not include result field..");
- }
- return out.getBoolean(DocumentsContract.EXTRA_RESULT);
+ /** @removed */
+ public static boolean isChildDocument(ContentResolver content, Uri parentDocumentUri,
+ Uri childDocumentUri) throws FileNotFoundException {
+ return isChildDocument((ContentInterface) content, parentDocumentUri, childDocumentUri);
}
/**
@@ -1301,31 +1364,28 @@
* @return the existing or new document after the rename, or {@code null} if
* failed.
*/
- public static Uri renameDocument(ContentResolver resolver, Uri documentUri,
+ public static Uri renameDocument(ContentInterface content, Uri documentUri,
String displayName) throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- documentUri.getAuthority());
try {
- return renameDocument(client, documentUri, displayName);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+ in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
+
+ final Bundle out = content.call(documentUri.getAuthority(),
+ METHOD_RENAME_DOCUMENT, null, in);
+ final Uri outUri = out.getParcelable(DocumentsContract.EXTRA_URI);
+ return (outUri != null) ? outUri : documentUri;
} catch (Exception e) {
Log.w(TAG, "Failed to rename document", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- public static Uri renameDocument(ContentProviderClient client, Uri documentUri,
- String displayName) throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
- in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
-
- final Bundle out = client.call(METHOD_RENAME_DOCUMENT, null, in);
- final Uri outUri = out.getParcelable(DocumentsContract.EXTRA_URI);
- return (outUri != null) ? outUri : documentUri;
+ /** @removed */
+ public static Uri renameDocument(ContentResolver content, Uri documentUri,
+ String displayName) throws FileNotFoundException {
+ return renameDocument((ContentInterface) content, documentUri, displayName);
}
/**
@@ -1334,29 +1394,26 @@
* @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
* @return if the document was deleted successfully.
*/
- public static boolean deleteDocument(ContentResolver resolver, Uri documentUri)
+ public static boolean deleteDocument(ContentInterface content, Uri documentUri)
throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- documentUri.getAuthority());
try {
- deleteDocument(client, documentUri);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+
+ content.call(documentUri.getAuthority(),
+ METHOD_DELETE_DOCUMENT, null, in);
return true;
} catch (Exception e) {
Log.w(TAG, "Failed to delete document", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return false;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- public static void deleteDocument(ContentProviderClient client, Uri documentUri)
- throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
-
- client.call(METHOD_DELETE_DOCUMENT, null, in);
+ /** @removed */
+ public static boolean deleteDocument(ContentResolver content, Uri documentUri)
+ throws FileNotFoundException {
+ return deleteDocument((ContentInterface) content, documentUri);
}
/**
@@ -1367,30 +1424,27 @@
* document's copy.
* @return the copied document, or {@code null} if failed.
*/
- public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri,
+ public static Uri copyDocument(ContentInterface content, Uri sourceDocumentUri,
Uri targetParentDocumentUri) throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- sourceDocumentUri.getAuthority());
try {
- return copyDocument(client, sourceDocumentUri, targetParentDocumentUri);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
+ in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
+
+ final Bundle out = content.call(sourceDocumentUri.getAuthority(),
+ METHOD_COPY_DOCUMENT, null, in);
+ return out.getParcelable(DocumentsContract.EXTRA_URI);
} catch (Exception e) {
Log.w(TAG, "Failed to copy document", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- public static Uri copyDocument(ContentProviderClient client, Uri sourceDocumentUri,
- Uri targetParentDocumentUri) throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
- in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
-
- final Bundle out = client.call(METHOD_COPY_DOCUMENT, null, in);
- return out.getParcelable(DocumentsContract.EXTRA_URI);
+ /** @removed */
+ public static Uri copyDocument(ContentResolver content, Uri sourceDocumentUri,
+ Uri targetParentDocumentUri) throws FileNotFoundException {
+ return copyDocument((ContentInterface) content, sourceDocumentUri, targetParentDocumentUri);
}
/**
@@ -1402,33 +1456,29 @@
* document.
* @return the moved document, or {@code null} if failed.
*/
- public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
+ public static Uri moveDocument(ContentInterface content, Uri sourceDocumentUri,
Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- sourceDocumentUri.getAuthority());
try {
- return moveDocument(client, sourceDocumentUri, sourceParentDocumentUri,
- targetParentDocumentUri);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
+ in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
+ in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
+
+ final Bundle out = content.call(sourceDocumentUri.getAuthority(),
+ METHOD_MOVE_DOCUMENT, null, in);
+ return out.getParcelable(DocumentsContract.EXTRA_URI);
} catch (Exception e) {
Log.w(TAG, "Failed to move document", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- @UnsupportedAppUsage
- public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri,
- Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
- in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
- in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
-
- final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in);
- return out.getParcelable(DocumentsContract.EXTRA_URI);
+ /** @removed */
+ public static Uri moveDocument(ContentResolver content, Uri sourceDocumentUri,
+ Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
+ return moveDocument((ContentInterface) content, sourceDocumentUri, sourceParentDocumentUri,
+ targetParentDocumentUri);
}
/**
@@ -1441,30 +1491,27 @@
* @param parentDocumentUri parent document of the document to remove.
* @return true if the document was removed successfully.
*/
- public static boolean removeDocument(ContentResolver resolver, Uri documentUri,
+ public static boolean removeDocument(ContentInterface content, Uri documentUri,
Uri parentDocumentUri) throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- documentUri.getAuthority());
try {
- removeDocument(client, documentUri, parentDocumentUri);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+ in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, parentDocumentUri);
+
+ content.call(documentUri.getAuthority(),
+ METHOD_REMOVE_DOCUMENT, null, in);
return true;
} catch (Exception e) {
Log.w(TAG, "Failed to remove document", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return false;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- public static void removeDocument(ContentProviderClient client, Uri documentUri,
- Uri parentDocumentUri) throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
- in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, parentDocumentUri);
-
- client.call(METHOD_REMOVE_DOCUMENT, null, in);
+ /** @removed */
+ public static boolean removeDocument(ContentResolver content, Uri documentUri,
+ Uri parentDocumentUri) throws FileNotFoundException {
+ return removeDocument((ContentInterface) content, documentUri, parentDocumentUri);
}
/**
@@ -1472,37 +1519,33 @@
*
* @param rootUri root with {@link Root#FLAG_SUPPORTS_EJECT} to be ejected
*/
- public static void ejectRoot(ContentResolver resolver, Uri rootUri) {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- rootUri.getAuthority());
+ public static void ejectRoot(ContentInterface content, Uri rootUri) {
try {
- ejectRoot(client, rootUri);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, rootUri);
+
+ content.call(rootUri.getAuthority(),
+ METHOD_EJECT_ROOT, null, in);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /** {@hide} */
- public static void ejectRoot(ContentProviderClient client, Uri rootUri)
- throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, rootUri);
-
- client.call(METHOD_EJECT_ROOT, null, in);
+ /** @removed */
+ public static void ejectRoot(ContentResolver content, Uri rootUri) {
+ ejectRoot((ContentInterface) content, rootUri);
}
/**
* Returns metadata associated with the document. The type of metadata returned
* is specific to the document type. For example the data returned for an image
- * file will likely consist primarily or soley of EXIF metadata.
+ * file will likely consist primarily or solely of EXIF metadata.
*
* <p>The returned {@link Bundle} will contain zero or more entries depending
* on the type of data supported by the document provider.
*
* <ol>
- * <li>A {@link DocumentsContract.METADATA_TYPES} containing a {@code String[]} value.
+ * <li>A {@link DocumentsContract#METADATA_TYPES} containing a {@code String[]} value.
* The string array identifies the type or types of metadata returned. Each
* value in the can be used to access a {@link Bundle} of data
* containing that type of data.
@@ -1522,66 +1565,26 @@
*
* @param documentUri a Document URI
* @return a Bundle of Bundles.
- * {@hide}
*/
- public static Bundle getDocumentMetadata(ContentResolver resolver, Uri documentUri)
+ public static Bundle getDocumentMetadata(ContentInterface content, Uri documentUri)
throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- documentUri.getAuthority());
-
try {
- return getDocumentMetadata(client, documentUri);
+ final Bundle in = new Bundle();
+ in.putParcelable(EXTRA_URI, documentUri);
+
+ return content.call(documentUri.getAuthority(),
+ METHOD_GET_DOCUMENT_METADATA, null, in);
} catch (Exception e) {
Log.w(TAG, "Failed to get document metadata");
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /**
- * Returns metadata associated with the document. The type of metadata returned
- * is specific to the document type. For example the data returned for an image
- * file will likely consist primarily or soley of EXIF metadata.
- *
- * <p>The returned {@link Bundle} will contain zero or more entries depending
- * on the type of data supported by the document provider.
- *
- * <ol>
- * <li>A {@link DocumentsContract.METADATA_TYPES} containing a {@code String[]} value.
- * The string array identifies the type or types of metadata returned. Each
- * value in the can be used to access a {@link Bundle} of data
- * containing that type of data.
- * <li>An entry each for each type of returned metadata. Each set of metadata is
- * itself represented as a bundle and accessible via a string key naming
- * the type of data.
- * </ol>
- *
- * <p>Example:
- * <p><pre><code>
- * Bundle metadata = DocumentsContract.getDocumentMetadata(client, imageDocUri, tags);
- * if (metadata.containsKey(DocumentsContract.METADATA_EXIF)) {
- * Bundle exif = metadata.getBundle(DocumentsContract.METADATA_EXIF);
- * int imageLength = exif.getInt(ExifInterface.TAG_IMAGE_LENGTH);
- * }
- * </code></pre>
- *
- * @param documentUri a Document URI
- * @return a Bundle of Bundles.
- * {@hide}
- */
- public static Bundle getDocumentMetadata(
- ContentProviderClient client, Uri documentUri) throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(EXTRA_URI, documentUri);
-
- final Bundle out = client.call(METHOD_GET_DOCUMENT_METADATA, null, in);
-
- if (out == null) {
- throw new RemoteException("Failed to get a response from getDocumentMetadata");
- }
- return out;
+ /** @removed */
+ public static Bundle getDocumentMetadata(ContentResolver content, Uri documentUri)
+ throws FileNotFoundException {
+ return getDocumentMetadata((ContentInterface) content, documentUri);
}
/**
@@ -1597,49 +1600,28 @@
* @return the path of the document, or {@code null} if failed.
* @see DocumentsProvider#findDocumentPath(String, String)
*/
- public static Path findDocumentPath(ContentResolver resolver, Uri treeUri)
+ public static Path findDocumentPath(ContentInterface content, Uri treeUri)
throws FileNotFoundException {
checkArgument(isTreeUri(treeUri), treeUri + " is not a tree uri.");
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- treeUri.getAuthority());
try {
- return findDocumentPath(client, treeUri);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, treeUri);
+
+ final Bundle out = content.call(treeUri.getAuthority(),
+ METHOD_FIND_DOCUMENT_PATH, null, in);
+ return out.getParcelable(DocumentsContract.EXTRA_RESULT);
} catch (Exception e) {
Log.w(TAG, "Failed to find path", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /**
- * Finds the canonical path. If uri is a document uri returns path from a root and
- * its associated root id. If uri is a tree uri returns the path from the top of
- * the tree. The {@link Path#getPath()} of the return value contains document ID
- * starts from the top of the tree or the root document to the requested document,
- * both inclusive.
- *
- * Callers can expect the root ID returned from multiple calls to this method is
- * consistent.
- *
- * @param uri uri of the document which path is requested. It can be either a
- * plain document uri or a tree uri.
- * @return the path of the document.
- * @see DocumentsProvider#findDocumentPath(String, String)
- *
- * {@hide}
- */
- public static Path findDocumentPath(ContentProviderClient client, Uri uri)
- throws RemoteException {
-
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, uri);
-
- final Bundle out = client.call(METHOD_FIND_DOCUMENT_PATH, null, in);
-
- return out.getParcelable(DocumentsContract.EXTRA_RESULT);
+ /** @removed */
+ public static Path findDocumentPath(ContentResolver content, Uri treeUri)
+ throws FileNotFoundException {
+ return findDocumentPath((ContentInterface) content, treeUri);
}
/**
@@ -1693,37 +1675,32 @@
* @see DocumentsProvider#createWebLinkIntent(String, Bundle)
* @see Intent#EXTRA_EMAIL
*/
- public static IntentSender createWebLinkIntent(ContentResolver resolver, Uri uri,
+ public static IntentSender createWebLinkIntent(ContentInterface content, Uri uri,
Bundle options) throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- uri.getAuthority());
try {
- return createWebLinkIntent(client, uri, options);
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, uri);
+
+ // Options may be provider specific, so put them in a separate bundle to
+ // avoid overriding the Uri.
+ if (options != null) {
+ in.putBundle(EXTRA_OPTIONS, options);
+ }
+
+ final Bundle out = content.call(uri.getAuthority(),
+ METHOD_CREATE_WEB_LINK_INTENT, null, in);
+ return out.getParcelable(DocumentsContract.EXTRA_RESULT);
} catch (Exception e) {
Log.w(TAG, "Failed to create a web link intent", e);
- rethrowIfNecessary(resolver, e);
+ rethrowIfNecessary(e);
return null;
- } finally {
- ContentProviderClient.releaseQuietly(client);
}
}
- /**
- * {@hide}
- */
- public static IntentSender createWebLinkIntent(ContentProviderClient client, Uri uri,
- Bundle options) throws RemoteException {
- final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, uri);
-
- // Options may be provider specific, so put them in a separate bundle to
- // avoid overriding the Uri.
- if (options != null) {
- in.putBundle(EXTRA_OPTIONS, options);
- }
-
- final Bundle out = client.call(METHOD_CREATE_WEB_LINK_INTENT, null, in);
- return out.getParcelable(DocumentsContract.EXTRA_RESULT);
+ /** @removed */
+ public static IntentSender createWebLinkIntent(ContentResolver content, Uri uri,
+ Bundle options) throws FileNotFoundException {
+ return createWebLinkIntent((ContentInterface) content, uri, options);
}
/**
@@ -1766,10 +1743,9 @@
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
}
- private static void rethrowIfNecessary(ContentResolver resolver, Exception e)
- throws FileNotFoundException {
+ private static void rethrowIfNecessary(Exception e) throws FileNotFoundException {
// We only want to throw applications targetting O and above
- if (resolver.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
+ if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.O) {
if (e instanceof ParcelableException) {
((ParcelableException) e).maybeRethrow(FileNotFoundException.class);
} else if (e instanceof RemoteException) {
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 58f8213..70c84f8 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -677,6 +677,7 @@
* cursor. If {@code null} all supported columns should be
* included.
* @param queryArgs the query arguments.
+ * {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA},
* {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
* {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
* {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER},
@@ -690,7 +691,6 @@
* @see DocumentsContract#EXTRA_LOADING
* @see DocumentsContract#EXTRA_INFO
* @see DocumentsContract#EXTRA_ERROR
- * {@hide}
*/
@SuppressWarnings("unused")
public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
@@ -710,7 +710,28 @@
throw new UnsupportedOperationException("Eject not supported");
}
- /** {@hide} */
+ /**
+ * Returns metadata associated with the document. The type of metadata returned
+ * is specific to the document type. For example the data returned for an image
+ * file will likely consist primarily or solely of EXIF metadata.
+ *
+ * <p>The returned {@link Bundle} will contain zero or more entries depending
+ * on the type of data supported by the document provider.
+ *
+ * <ol>
+ * <li>A {@link DocumentsContract#METADATA_TYPES} containing a {@code String[]} value.
+ * The string array identifies the type or types of metadata returned. Each
+ * value in the can be used to access a {@link Bundle} of data
+ * containing that type of data.
+ * <li>An entry each for each type of returned metadata. Each set of metadata is
+ * itself represented as a bundle and accessible via a string key naming
+ * the type of data.
+ * </ol>
+ *
+ * @param documentId get the metadata of the document
+ * @return a Bundle of Bundles.
+ * @see DocumentsContract#getDocumentMetadata(ContentResolver, Uri)
+ */
public @Nullable Bundle getDocumentMetadata(String documentId)
throws FileNotFoundException {
throw new UnsupportedOperationException("Metadata not supported");
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index ec8db1c..f38f740 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -16,11 +16,14 @@
package android.provider;
+import android.annotation.BytesLong;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.AppGlobals;
@@ -37,6 +40,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
+import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -45,6 +49,7 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
@@ -65,6 +70,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* The Media provider contains meta data for all available media on both internal
@@ -101,6 +107,11 @@
/** {@hide} */
public static final String GET_MEDIA_URI_CALL = "get_media_uri";
+ /** {@hide} */
+ public static final String GET_CONTRIBUTED_MEDIA_CALL = "get_contributed_media";
+ /** {@hide} */
+ public static final String DELETE_CONTRIBUTED_MEDIA_CALL = "delete_contributed_media";
+
/**
* This is for internal use by the media scanner only.
* Name of the (optional) Uri parameter that determines whether to skip deleting
@@ -1036,6 +1047,20 @@
getContentUri("external");
/**
+ * The MIME type for this table.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/download";
+
+ /**
+ * Regex that matches paths that needs to be considered part of downloads collection.
+ * @hide
+ */
+ public static final Pattern PATTERN_DOWNLOADS_FILE = Pattern.compile(
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/.+");
+ private static final Pattern PATTERN_DOWNLOADS_DIRECTORY = Pattern.compile(
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/?");
+
+ /**
* Get the content:// style URI for the downloads table on the
* given volume.
*
@@ -1051,6 +1076,16 @@
public static Uri getContentUriForPath(@NonNull String path) {
return getContentUri(getVolumeNameForPath(path));
}
+
+ /** @hide */
+ public static boolean isDownload(@NonNull String path) {
+ return PATTERN_DOWNLOADS_FILE.matcher(path).matches();
+ }
+
+ /** @hide */
+ public static boolean isDownloadDir(@NonNull String path) {
+ return PATTERN_DOWNLOADS_DIRECTORY.matcher(path).matches();
+ }
}
private static String getVolumeNameForPath(@NonNull String path) {
@@ -1166,13 +1201,25 @@
/**
* The latitude where the image was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LATITUDE = "latitude";
/**
* The longitude where the image was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LONGITUDE = "longitude";
/**
@@ -1693,6 +1740,12 @@
public static final String IS_NOTIFICATION = "is_notification";
/**
+ * Non-zero if the audio file is an audiobook
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String IS_AUDIOBOOK = "is_audiobook";
+
+ /**
* The genre of the audio file, if any
* <P>Type: TEXT</P>
* Does not exist in the database - only used by the media scanner for inserts.
@@ -2404,13 +2457,25 @@
/**
* The latitude where the video was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LATITUDE = "latitude";
/**
* The longitude where the video was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LONGITUDE = "longitude";
/**
@@ -2834,4 +2899,59 @@
throw e.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Calculate size of media contributed by given package under the calling
+ * user. The meaning of "contributed" means it won't automatically be
+ * deleted when the app is uninstalled.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
+ public static @BytesLong long getContributedMediaSize(Context context, String packageName,
+ UserHandle user) throws IOException {
+ final UserManager um = context.getSystemService(UserManager.class);
+ if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
+ try {
+ final ContentResolver resolver = context
+ .createPackageContextAsUser(packageName, 0, user).getContentResolver();
+ final Bundle in = new Bundle();
+ in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ final Bundle out = resolver.call(AUTHORITY, GET_CONTRIBUTED_MEDIA_CALL, null, in);
+ return out.getLong(Intent.EXTRA_INDEX);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ } else {
+ throw new IOException("User " + user + " must be unlocked and running");
+ }
+ }
+
+ /**
+ * Delete all media contributed by given package under the calling user. The
+ * meaning of "contributed" means it won't automatically be deleted when the
+ * app is uninstalled.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
+ public static void deleteContributedMedia(Context context, String packageName,
+ UserHandle user) throws IOException {
+ final UserManager um = context.getSystemService(UserManager.class);
+ if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
+ try {
+ final ContentResolver resolver = context
+ .createPackageContextAsUser(packageName, 0, user).getContentResolver();
+ final Bundle in = new Bundle();
+ in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ resolver.call(AUTHORITY, DELETE_CONTRIBUTED_MEDIA_CALL, null, in);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ } else {
+ throw new IOException("User " + user + " must be unlocked and running");
+ }
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c297ef4..93a5950 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1680,6 +1680,11 @@
*/
public static final String CALL_METHOD_TAG_KEY = "_tag";
+ /**
+ * @hide - String argument extra to the fast-path call()-based requests
+ */
+ public static final String CALL_METHOD_PREFIX_KEY = "_prefix";
+
/** @hide - Private call() method to write to 'system' table */
public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
@@ -1701,15 +1706,18 @@
/** @hide - Private call() method to delete from the 'global' table */
public static final String CALL_METHOD_DELETE_GLOBAL = "DELETE_global";
+ /** @hide - Private call() method to reset to defaults the 'configuration' table */
+ public static final String CALL_METHOD_DELETE_CONFIG = "DELETE_config";
+
+ /** @hide - Private call() method to reset to defaults the 'secure' table */
+ public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
+
/** @hide - Private call() method to reset to defaults the 'global' table */
public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
/** @hide - Private call() method to reset to defaults the 'configuration' table */
public static final String CALL_METHOD_RESET_CONFIG = "RESET_config";
- /** @hide - Private call() method to reset to defaults the 'secure' table */
- public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
-
/** @hide - Private call() method to query the 'system' table */
public static final String CALL_METHOD_LIST_SYSTEM = "LIST_system";
@@ -1719,6 +1727,9 @@
/** @hide - Private call() method to query the 'global' table */
public static final String CALL_METHOD_LIST_GLOBAL = "LIST_global";
+ /** @hide - Private call() method to reset to defaults the 'configuration' table */
+ public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
+
/**
* Activity Extra: Limit available options in launched activity based on the given authority.
* <p>
@@ -2044,7 +2055,8 @@
arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
- cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
+ cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+ mCallSetCommand, name, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -2117,12 +2129,14 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
+ b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+ mCallGetCommand, name, args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
+ b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+ mCallGetCommand, name, args);
}
if (b != null) {
String value = b.getString(Settings.NameValueTable.VALUE);
@@ -4252,6 +4266,7 @@
PUBLIC_SETTINGS.add(BLUETOOTH_DISCOVERABILITY_TIMEOUT);
PUBLIC_SETTINGS.add(NEXT_ALARM_FORMATTED);
PUBLIC_SETTINGS.add(FONT_SCALE);
+ PUBLIC_SETTINGS.add(SYSTEM_LOCALES);
PUBLIC_SETTINGS.add(DIM_SCREEN);
PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT);
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS);
@@ -5111,7 +5126,8 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), CALL_METHOD_RESET_SECURE, null, arg);
+ cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_RESET_SECURE, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
}
@@ -6279,6 +6295,7 @@
*
* @hide
*/
+ @SystemApi
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
"accessibility_display_magnification_navbar_enabled";
@@ -11861,10 +11878,26 @@
public static final String GPU_DEBUG_APP = "gpu_debug_app";
/**
- * App should try to use ANGLE
+ * Force all PKGs to use ANGLE, regardless of any other settings
+ * The value is a boolean (1 or 0).
* @hide
*/
- public static final String ANGLE_ENABLED_APP = "angle_enabled_app";
+ public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE =
+ "angle_gl_driver_all_angle";
+
+ /**
+ * List of PKGs that have an OpenGL driver selected
+ * @hide
+ */
+ public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS =
+ "angle_gl_driver_selection_pkgs";
+
+ /**
+ * List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS
+ * @hide
+ */
+ public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES =
+ "angle_gl_driver_selection_values";
/**
* App that is selected to use updated graphics driver.
@@ -12819,6 +12852,13 @@
public static final String HIDDEN_API_POLICY = "hidden_api_policy";
/**
+ * Current version of signed configuration applied.
+ *
+ * @hide
+ */
+ public static final String SIGNED_CONFIG_VERSION = "signed_config_version";
+
+ /**
* Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
* operation (in ms).
*
@@ -12837,6 +12877,23 @@
"max_sound_trigger_detection_service_ops_per_day";
/**
+ * Property used by {@code com.android.server.SystemServer} on start to decide whether
+ * the Content Capture service should be created or not
+ *
+ * <p>By default it should *NOT* be set (in which case the decision is based on whether
+ * the OEM provides an implementation for the service), but it can be overridden to:
+ *
+ * <ul>
+ * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency.
+ * <li>Enable the CTS tests to be run on AOSP builds
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED =
+ "content_capture_service_explicitly_enabled";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -13128,7 +13185,8 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), CALL_METHOD_RESET_GLOBAL, null, arg);
+ cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_RESET_GLOBAL, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
}
@@ -13509,6 +13567,13 @@
*/
public static final String WARNING_TEMPERATURE = "warning_temperature";
+
+ /**
+ * USB Temperature at which the high temperature alarm notification should be shown.
+ * @hide
+ */
+ public static final String USB_ALARM_TEMPERATURE = "usb_alarm_temperature";
+
/**
* Whether the diskstats logging task is enabled/disabled.
* @hide
@@ -13645,6 +13710,22 @@
"smart_replies_in_notifications_flags";
/**
+ * Configuration flags for the automatic generation of smart replies and smart actions in
+ * notifications. This is encoded as a key=value list, separated by commas. Ex:
+ * "generate_replies=false,generate_actions=true".
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * generate_replies (boolean)
+ * generate_actions (boolean)
+ * </pre>
+ * @hide
+ */
+ public static final String SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS =
+ "smart_suggestions_in_notifications_flags";
+
+ /**
* If nonzero, crashes in foreground processes will bring up a dialog.
* Otherwise, the process will be silently killed.
* @hide
@@ -13721,6 +13802,14 @@
"backup_agent_timeout_parameters";
/**
+ * Whether the backup system service supports multiple users (0 = disabled, 1 = enabled). If
+ * disabled, the service will only be active for the system user.
+ *
+ * @hide
+ */
+ public static final String BACKUP_MULTI_USER_ENABLED = "backup_multi_user_enabled";
+
+ /**
* Blacklist of GNSS satellites.
*
* This is a list of integers separated by commas to represent pairs of (constellation,
@@ -13844,13 +13933,6 @@
/**
* Store a name/value pair into the database.
* <p>
- * The method takes an optional tag to associate with the setting which can be used to clear
- * only settings made by your package and associated with this tag by passing the tag to
- * {@link #resetToDefaults(ContentResolver, String)}. The value of this setting can be
- * overridden by future calls to this or other put methods, and the tag provided in those
- * calls, which may be null, will override the tag provided in this call. Any call to a put
- * method which does not accept a tag will effectively set the tag to null.
- * </p><p>
* Also the method takes an argument whether to make the value the default for this setting.
* If the system already specified a default value, then the one passed in here will
* <strong>not</strong> be set as the default.
@@ -13859,48 +13941,50 @@
* @param resolver to access the database with.
* @param name to store.
* @param value to associate with the name.
- * @param tag to associated with the setting.
* @param makeDefault whether to make the value the default one.
* @return true if the value was set, false on database errors.
*
- * @see #resetToDefaults(ContentResolver, String)
+ * @see #resetToDefaults(ContentResolver, int, String)
*
* @hide
*/
// TODO(b/117663715): require a new write permission restricted to a single source
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- static boolean putString(@NonNull ContentResolver resolver,
- @NonNull String name, @Nullable String value, @Nullable String tag,
- boolean makeDefault) {
- return sNameValueCache.putStringForUser(resolver, name, value, tag, makeDefault,
+ static boolean putString(@NonNull ContentResolver resolver, @NonNull String name,
+ @Nullable String value, boolean makeDefault) {
+ return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault,
resolver.getUserId());
}
/**
- * Reset the settings to their defaults. This would reset <strong>only</strong> settings set
- * by the caller's package. Passing in the optional tag will reset only settings changed by
- * your package and associated with this tag.
+ * Reset the values to their defaults.
+ * <p>
+ * The method accepts an optional prefix parameter. If provided, only pairs with a name that
+ * starts with the exact prefix will be reset. Otherwise all will be reset.
*
* @param resolver Handle to the content resolver.
- * @param tag Optional tag which should be associated with the settings to reset.
+ * @param resetMode The reset mode to use.
+ * @param prefix Optionally, to limit which which pairs are reset.
*
- * @see #putString(ContentResolver, String, String, String, boolean)
+ * @see #putString(ContentResolver, String, String, boolean)
*
* @hide
*/
// TODO(b/117663715): require a new write permission restricted to a single source
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- static void resetToDefaults(@NonNull ContentResolver resolver,
- @Nullable String tag) {
+ static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
+ @Nullable String prefix) {
try {
Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
- if (tag != null) {
- arg.putString(CALL_METHOD_TAG_KEY, tag);
+ arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
+ if (prefix != null) {
+ arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
}
- arg.putInt(CALL_METHOD_RESET_MODE_KEY, RESET_MODE_PACKAGE_DEFAULTS);
+ arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), CALL_METHOD_RESET_CONFIG, null, arg);
+ cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_RESET_CONFIG, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI, e);
}
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
deleted file mode 100644
index 8e67ce7..0000000
--- a/core/java/android/provider/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.provider.SettingsBackupTest"
- }
- ]
- }
- ]
-}
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 1cd76d2..2612917 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
@@ -35,6 +36,7 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* A service that calculates field classification scores.
@@ -51,6 +53,7 @@
* {@hide}
*/
@SystemApi
+@TestApi
public abstract class AutofillFieldClassificationService extends Service {
private static final String TAG = "AutofillFieldClassificationService";
@@ -75,17 +78,32 @@
public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
"android.autofill.field_classification.available_algorithms";
+ /**
+ * Field classification algorithm that computes the edit distance between two Strings.
+ *
+ * <p>Service implementation must provide this algorithm.</p>
+ */
+ public static final String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
+
+ /**
+ * Field classification algorithm that computes whether the last four digits between two
+ * Strings match exactly.
+ *
+ * <p>Service implementation must provide this algorithm.</p>
+ */
+ public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
/** {@hide} **/
public static final String EXTRA_SCORES = "scores";
private AutofillFieldClassificationServiceWrapper mWrapper;
- private void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
- List<AutofillValue> actualValues, String[] userDataValues) {
+ private void calculateScores(RemoteCallback callback, List<AutofillValue> actualValues,
+ String[] userDataValues, String[] categoryIds, String defaultAlgorithm,
+ Bundle defaultArgs, Map algorithms, Map args) {
final Bundle data = new Bundle();
- final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
- Arrays.asList(userDataValues));
+ final float[][] scores = onCalculateScores(actualValues, Arrays.asList(userDataValues),
+ Arrays.asList(categoryIds), defaultAlgorithm, defaultArgs, algorithms, args);
if (scores != null) {
data.putParcelable(EXTRA_SCORES, new Scores(scores));
}
@@ -169,26 +187,111 @@
* @return the calculated scores of {@code actualValues} x {@code userDataValues}.
*
* {@hide}
+ *
+ * @deprecated Use {@link AutofillFieldClassificationService#onCalculateScores} instead.
*/
@Nullable
@SystemApi
+ @Deprecated
public float[][] onGetScores(@Nullable String algorithm,
@Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
- Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
+ Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScores()");
+ return null;
+ }
+
+ /**
+ * Calculates field classification scores in a batch.
+ *
+ * <p>A field classification score is a {@code float} representing how well an
+ * {@link AutofillValue} matches a expected value predicted by an autofill service
+ * —a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
+ *
+ * <p>The exact score depends on the algorithm used to calculate it—the service must
+ * provide at least one default algorithm (which is used when the algorithm is not specified
+ * or is invalid), but it could provide more (in which case the algorithm name should be
+ * specified by the caller when calculating the scores).
+ *
+ * <p>For example, if the service provides an algorithm named {@code EXACT_MATCH} that
+ * returns {@code 1.0} if all characters match or {@code 0.0} otherwise, a call to:
+ *
+ * <pre>
+ * HashMap algorithms = new HashMap<>();
+ * algorithms.put("email", "EXACT_MATCH");
+ * algorithms.put("phone", "EXACT_MATCH");
+ *
+ * HashMap args = new HashMap<>();
+ * args.put("email", null);
+ * args.put("phone", null);
+ *
+ * service.onCalculateScores(Arrays.asList(AutofillValue.forText("email1"),
+ * AutofillValue.forText("PHONE1")), Arrays.asList("email1", "phone1"),
+ * Array.asList("email", "phone"), algorithms, args);
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 0.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * <p>If the same algorithm allows the caller to specify whether the comparisons should be
+ * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to:
+ *
+ * <pre>
+ * Bundle algorithmOptions = new Bundle();
+ * algorithmOptions.putBoolean("case_sensitive", false);
+ * args.put("phone", algorithmOptions);
+ *
+ * service.onCalculateScores(Arrays.asList(AutofillValue.forText("email1"),
+ * AutofillValue.forText("PHONE1")), Arrays.asList("email1", "phone1"),
+ * Array.asList("email", "phone"), algorithms, args);
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 1.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * @param actualValues values entered by the user.
+ * @param userDataValues values predicted from the user data.
+ * @param categoryIds category Ids correspoinding to userDataValues
+ * @param defaultAlgorithm default field classification algorithm
+ * @param algorithms array of field classification algorithms
+ * @return the calculated scores of {@code actualValues} x {@code userDataValues}.
+ *
+ * {@hide}
+ */
+ @Nullable
+ @SystemApi
+ public float[][] onCalculateScores(@NonNull List<AutofillValue> actualValues,
+ @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable Map algorithms, @Nullable Map args) {
+ Log.e(TAG, "service implementation (" + getClass()
+ + " does not implement onCalculateScore()");
return null;
}
private final class AutofillFieldClassificationServiceWrapper
extends IAutofillFieldClassificationService.Stub {
@Override
- public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
- List<AutofillValue> actualValues, String[] userDataValues)
- throws RemoteException {
+ public void calculateScores(RemoteCallback callback, List<AutofillValue> actualValues,
+ String[] userDataValues, String[] categoryIds, String defaultAlgorithm,
+ Bundle defaultArgs, Map algorithms, Map args)
+ throws RemoteException {
mHandler.sendMessage(obtainMessage(
- AutofillFieldClassificationService::getScores,
+ AutofillFieldClassificationService::calculateScores,
AutofillFieldClassificationService.this,
- callback, algorithmName, algorithmArgs, actualValues, userDataValues));
+ callback, actualValues, userDataValues, categoryIds, defaultAlgorithm,
+ defaultArgs, algorithms, args));
}
}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 573d577..f39ef9a 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -618,7 +618,7 @@
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
}
- Log.w(TAG, "Tried to bind to wrong intent: " + intent);
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
return null;
}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 7bf1f83..d408e9a 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -84,6 +84,7 @@
private final @Nullable AutofillId[] mFieldClassificationIds;
private final int mFlags;
private int mRequestId;
+ private final @Nullable UserData mUserData;
private FillResponse(@NonNull Builder builder) {
mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -99,6 +100,7 @@
mFieldClassificationIds = builder.mFieldClassificationIds;
mFlags = builder.mFlags;
mRequestId = INVALID_REQUEST_ID;
+ mUserData = builder.mUserData;
}
/** @hide */
@@ -157,6 +159,11 @@
}
/** @hide */
+ public @Nullable UserData getUserData() {
+ return mUserData;
+ }
+
+ /** @hide */
@TestApi
public int getFlags() {
return mFlags;
@@ -198,6 +205,7 @@
private AutofillId[] mFieldClassificationIds;
private int mFlags;
private boolean mDestroyed;
+ private UserData mUserData;
/**
* Triggers a custom UI before before autofilling the screen with any data set in this
@@ -506,6 +514,21 @@
}
/**
+ * Sets a specific {@link UserData} for field classification for this request only.
+ *
+ * @return this builder
+ * @throws IllegalStateException if the FillResponse
+ * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+ * requires authentication}.
+ */
+ public Builder setUserData(@NonNull UserData userData) {
+ throwIfDestroyed();
+ throwIfAuthenticationCalled();
+ mUserData = Preconditions.checkNotNull(userData);
+ return this;
+ }
+
+ /**
* Builds a new {@link FillResponse} instance.
*
* @throws IllegalStateException if any of the following conditions occur:
@@ -599,6 +622,9 @@
if (mFieldClassificationIds != null) {
builder.append(Arrays.toString(mFieldClassificationIds));
}
+ if (mUserData != null) {
+ builder.append(", userData=").append(mUserData);
+ }
return builder.append("]").toString();
}
@@ -621,6 +647,7 @@
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelable(mHeader, flags);
parcel.writeParcelable(mFooter, flags);
+ parcel.writeParcelable(mUserData, flags);
parcel.writeParcelableArray(mIgnoredIds, flags);
parcel.writeLong(mDisableDuration);
parcel.writeParcelableArray(mFieldClassificationIds, flags);
@@ -661,6 +688,10 @@
if (footer != null) {
builder.setFooter(footer);
}
+ final UserData userData = parcel.readParcelable(null);
+ if (userData != null) {
+ builder.setUserData(userData);
+ }
builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
final long disableDuration = parcel.readLong();
diff --git a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
index 398557d..2cd24f9 100644
--- a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
+++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
@@ -20,6 +20,7 @@
import android.os.RemoteCallback;
import android.view.autofill.AutofillValue;
import java.util.List;
+import java.util.Map;
/**
* Service used to calculate match scores for Autofill Field Classification.
@@ -27,6 +28,8 @@
* @hide
*/
oneway interface IAutofillFieldClassificationService {
- void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs,
- in List<AutofillValue> actualValues, in String[] userDataValues);
+ void calculateScores(in RemoteCallback callback, in List<AutofillValue> actualValues,
+ in String[] userDataValues, in String[] categoryIds,
+ in String defaultAlgorithm, in Bundle defaultArgs,
+ in Map algorithms, in Map args);
}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index fccb85b..37f1923 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.os.Bundle;
@@ -32,6 +33,7 @@
import android.provider.Settings;
import android.service.autofill.FieldClassification.Match;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.view.autofill.AutofillManager;
@@ -57,28 +59,57 @@
private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
private final String mId;
- private final String mAlgorithm;
- private final Bundle mAlgorithmArgs;
private final String[] mCategoryIds;
private final String[] mValues;
+ private final String mDefaultAlgorithm;
+ private final Bundle mDefaultArgs;
+ private final ArrayMap<String, String> mCategoryAlgorithms;
+ private final ArrayMap<String, Bundle> mCategoryArgs;
+
private UserData(Builder builder) {
mId = builder.mId;
- mAlgorithm = builder.mAlgorithm;
- mAlgorithmArgs = builder.mAlgorithmArgs;
mCategoryIds = new String[builder.mCategoryIds.size()];
builder.mCategoryIds.toArray(mCategoryIds);
mValues = new String[builder.mValues.size()];
builder.mValues.toArray(mValues);
+ builder.mValues.toArray(mValues);
+
+ mDefaultAlgorithm = builder.mDefaultAlgorithm;
+ mDefaultArgs = builder.mDefaultArgs;
+ mCategoryAlgorithms = builder.mCategoryAlgorithms;
+ mCategoryArgs = builder.mCategoryArgs;
}
/**
- * Gets the name of the algorithm that is used to calculate
- * {@link Match#getScore() match scores}.
+ * Gets the name of the default algorithm that is used to calculate
+ * {@link Match#getScore()} match scores}.
*/
@Nullable
public String getFieldClassificationAlgorithm() {
- return mAlgorithm;
+ return mDefaultAlgorithm;
+ }
+
+ /** @hide */
+ public Bundle getDefaultFieldClassificationArgs() {
+ return mDefaultArgs;
+ }
+
+ /**
+ * Gets the name of the algorithm corresponding to the specific autofill category
+ * that is used to calculate {@link Match#getScore() match scores}
+ *
+ * @param categoryId autofill field category
+ *
+ * @return String name of algorithm, null if none found.
+ */
+ @Nullable
+ public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
+ Preconditions.checkNotNull(categoryId);
+ if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
+ return null;
+ }
+ return mCategoryAlgorithms.get(categoryId);
}
/**
@@ -89,11 +120,6 @@
}
/** @hide */
- public Bundle getAlgorithmArgs() {
- return mAlgorithmArgs;
- }
-
- /** @hide */
public String[] getCategoryIds() {
return mCategoryIds;
}
@@ -104,11 +130,29 @@
}
/** @hide */
+ @TestApi
+ public ArrayMap<String, String> getFieldClassificationAlgorithms() {
+ return mCategoryAlgorithms;
+ }
+
+ /** @hide */
+ public ArrayMap<String, Bundle> getFieldClassificationArgs() {
+ return mCategoryArgs;
+ }
+
+ /** @hide */
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.print(mId);
- pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
- pw.print(" Args: "); pw.println(mAlgorithmArgs);
-
+ pw.print(prefix); pw.print("Default Algorithm: "); pw.print(mDefaultAlgorithm);
+ pw.print(prefix); pw.print("Default Args"); pw.print(mDefaultArgs);
+ if (mCategoryAlgorithms != null && mCategoryAlgorithms.size() > 0) {
+ pw.print(prefix); pw.print("Algorithms per category: ");
+ for (int i = 0; i < mCategoryAlgorithms.size(); i++) {
+ pw.print(prefix); pw.print(prefix); pw.print(mCategoryAlgorithms.keyAt(i));
+ pw.print(": "); pw.println(Helper.getRedacted(mCategoryAlgorithms.valueAt(i)));
+ pw.print("args="); pw.print(mCategoryArgs.get(mCategoryAlgorithms.keyAt(i)));
+ }
+ }
// Cannot disclose field ids or values because they could contain PII
pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length);
for (int i = 0; i < mCategoryIds.length; i++) {
@@ -139,8 +183,13 @@
private final String mId;
private final ArrayList<String> mCategoryIds;
private final ArrayList<String> mValues;
- private String mAlgorithm;
- private Bundle mAlgorithmArgs;
+ private String mDefaultAlgorithm;
+ private Bundle mDefaultArgs;
+
+ // Map of autofill field categories to fleid classification algorithms and args
+ private ArrayMap<String, String> mCategoryAlgorithms;
+ private ArrayMap<String, Bundle> mCategoryArgs;
+
private boolean mDestroyed;
// Non-persistent array used to limit the number of unique ids.
@@ -148,7 +197,6 @@
// Non-persistent array used to ignore duplaicated value/category pairs.
private final ArraySet<String> mUniqueValueCategoryPairs;
-
/**
* Creates a new builder for the user data used for <a href="#FieldClassification">field
* classification</a>.
@@ -169,7 +217,7 @@
* {@link AutofillManager#getUserData()}).
*
* @param value value of the user data.
- * @param categoryId string used to identify the category the value is associated with.
+ * @param categoryId autofill field category.
*
* @throws IllegalArgumentException if any of the following occurs:
* <ul>
@@ -189,13 +237,15 @@
mCategoryIds = new ArrayList<>(maxUserDataSize);
mValues = new ArrayList<>(maxUserDataSize);
mUniqueValueCategoryPairs = new ArraySet<>(maxUserDataSize);
+
mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount());
addMapping(value, categoryId);
}
/**
- * Sets the algorithm used for <a href="#FieldClassification">field classification</a>.
+ * Sets the default algorithm used for
+ * <a href="#FieldClassification">field classification</a>.
*
* <p>The currently available algorithms can be retrieve through
* {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
@@ -212,8 +262,40 @@
public Builder setFieldClassificationAlgorithm(@Nullable String name,
@Nullable Bundle args) {
throwIfDestroyed();
- mAlgorithm = name;
- mAlgorithmArgs = args;
+ mDefaultAlgorithm = name;
+ mDefaultArgs = args;
+ return this;
+ }
+
+ /**
+ * Sets the algorithm used for <a href="#FieldClassification">field classification</a>
+ * for the specified category.
+ *
+ * <p>The currently available algorithms can be retrieved through
+ * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
+ *
+ * <p>If not set, the
+ * {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is
+ * used instead.
+ *
+ * @param categoryId autofill field category.
+ * @param name name of the algorithm or {@code null} to used default.
+ * @param args optional arguments to the algorithm.
+ *
+ * @return this builder
+ */
+ public Builder setFieldClassificationAlgorithmForCategory(@NonNull String categoryId,
+ @Nullable String name, @Nullable Bundle args) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(categoryId);
+ if (mCategoryAlgorithms == null) {
+ mCategoryAlgorithms = new ArrayMap<>(getMaxCategoryCount());
+ }
+ if (mCategoryArgs == null) {
+ mCategoryArgs = new ArrayMap<>(getMaxCategoryCount());
+ }
+ mCategoryAlgorithms.put(categoryId, name);
+ mCategoryArgs.put(categoryId, args);
return this;
}
@@ -317,8 +399,7 @@
public String toString() {
if (!sDebug) return super.toString();
- final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId)
- .append(", algorithm=").append(mAlgorithm);
+ final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId);
// Cannot disclose category ids or values because they could contain PII
builder.append(", categoryIds=");
Helper.appendRedacted(builder, mCategoryIds);
@@ -341,8 +422,10 @@
parcel.writeString(mId);
parcel.writeStringArray(mCategoryIds);
parcel.writeStringArray(mValues);
- parcel.writeString(mAlgorithm);
- parcel.writeBundle(mAlgorithmArgs);
+ parcel.writeString(mDefaultAlgorithm);
+ parcel.writeBundle(mDefaultArgs);
+ parcel.writeMap(mCategoryAlgorithms);
+ parcel.writeMap(mCategoryArgs);
}
public static final Parcelable.Creator<UserData> CREATOR =
@@ -355,10 +438,28 @@
final String id = parcel.readString();
final String[] categoryIds = parcel.readStringArray();
final String[] values = parcel.readStringArray();
+ final String defaultAlgorithm = parcel.readString();
+ final Bundle defaultArgs = parcel.readBundle();
+ final ArrayMap<String, String> categoryAlgorithms = new ArrayMap<>();
+ parcel.readMap(categoryAlgorithms, String.class.getClassLoader());
+ final ArrayMap<String, Bundle> categoryArgs = new ArrayMap<>();
+ parcel.readMap(categoryArgs, Bundle.class.getClassLoader());
+
final Builder builder = new Builder(id, values[0], categoryIds[0])
- .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
+ .setFieldClassificationAlgorithm(defaultAlgorithm, defaultArgs);
+
for (int i = 1; i < categoryIds.length; i++) {
- builder.add(values[i], categoryIds[i]);
+ String categoryId = categoryIds[i];
+ builder.add(values[i], categoryId);
+ }
+
+ final int size = categoryAlgorithms.size();
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ final String categoryId = categoryAlgorithms.keyAt(i);
+ builder.setFieldClassificationAlgorithmForCategory(categoryId,
+ categoryAlgorithms.valueAt(i), categoryArgs.get(categoryId));
+ }
}
return builder.build();
}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
new file mode 100644
index 0000000..9aff281
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill.augmented;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.autofill.IAugmentedAutofillManagerClient;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service used to augment the Autofill subsystem by potentially providing autofill data when the
+ * "standard" workflow failed (for example, because the standard AutofillService didn't have data).
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AugmentedAutofillService extends Service {
+
+ private static final String TAG = AugmentedAutofillService.class.getSimpleName();
+
+ // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+ static final boolean DEBUG = true;
+ static final boolean VERBOSE = false;
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ * To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_AUGMENTED_AUTOFILL_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.autofill.augmented.AugmentedAutofillService";
+
+ private Handler mHandler;
+
+ private SparseArray<AutofillProxy> mAutofillProxies;
+
+ private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() {
+
+ @Override
+ public void onFillRequest(int sessionId, IBinder client, int taskId,
+ ComponentName componentName, AutofillId focusedId, AutofillValue focusedValue,
+ long requestTime, IFillCallback callback) {
+ mHandler.sendMessage(obtainMessage(AugmentedAutofillService::handleOnFillRequest,
+ AugmentedAutofillService.this, sessionId, client, taskId, componentName,
+ focusedId, focusedValue, requestTime, callback));
+ }
+
+ @Override
+ public void onDestroyFillWindowRequest(int sessionId) {
+ mHandler.sendMessage(
+ obtainMessage(AugmentedAutofillService::handleOnDestroyFillWindowRequest,
+ AugmentedAutofillService.this, sessionId));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ /** @hide */
+ @Override
+ public final IBinder onBind(Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ mHandler.sendMessage(obtainMessage(AugmentedAutofillService::handleOnUnbind,
+ AugmentedAutofillService.this));
+ return false;
+ }
+
+ // TODO(b/111330312): add methods to disable autofill per app / activity?
+
+ /**
+ * Asks the service to handle an "augmented" autofill request.
+ *
+ * <p>This method is called when the "stantard" autofill service cannot handle a request, which
+ * typically occurs when:
+ * <ul>
+ * <li>Service does not recognize what should be autofilled.
+ * <li>Service does not have data to fill the request.
+ * <li>Service blacklisted that app (or activity) for autofill.
+ * <li>App disabled itself for autofill.
+ * </ul>
+ *
+ * <p>Differently from the standard autofill workflow, on augmented autofill the service is
+ * responsible to generate the autofill UI and request the Android system to autofill the
+ * activity when the user taps an action in that UI (through the
+ * {@link FillController#autofill(List)} method).
+ *
+ * <p>The service <b>MUST</b> call {@link
+ * FillCallback#onSuccess(android.service.autofill.augmented.FillResponse)} as soon as possible,
+ * passing {@code null} when it cannot fulfill the request.
+ * @param request the request to handle.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param controller object used to interact with the autofill system.
+ * @param callback object used to notify the result of the request. Service <b>must</b> call
+ * {@link FillCallback#onSuccess(android.service.autofill.augmented.FillResponse)}.
+ */
+ public void onFillRequest(@NonNull FillRequest request,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller,
+ @NonNull FillCallback callback) {
+ }
+
+ private void handleOnFillRequest(int sessionId, @NonNull IBinder client, int taskId,
+ @NonNull ComponentName componentName, @NonNull AutofillId focusedId,
+ @Nullable AutofillValue focusedValue, long requestTime,
+ @NonNull IFillCallback callback) {
+ if (mAutofillProxies == null) {
+ mAutofillProxies = new SparseArray<>();
+ }
+ AutofillProxy proxy = mAutofillProxies.get(sessionId);
+ if (proxy == null) {
+ proxy = new AutofillProxy(sessionId, client, taskId, componentName, focusedId,
+ focusedValue, requestTime, callback);
+ mAutofillProxies.put(sessionId, proxy);
+ } else {
+ // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
+ // TODO(b/111330312): also make sure to cover scenario on CTS test
+ if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+ proxy.update(focusedId, focusedValue);
+ }
+ // TODO(b/111330312): set cancellation signal
+ final CancellationSignal cancellationSignal = null;
+ onFillRequest(new FillRequest(proxy), cancellationSignal, new FillController(proxy),
+ new FillCallback(proxy));
+ }
+
+ private void handleOnDestroyFillWindowRequest(@NonNull int sessionId) {
+ AutofillProxy proxy = null;
+ if (mAutofillProxies != null) {
+ proxy = mAutofillProxies.get(sessionId);
+ }
+ if (proxy == null) {
+ // TODO(b/111330312): this might be fine, in which case we should logv it
+ Log.w(TAG, "No proxy for session " + sessionId);
+ return;
+ }
+ proxy.destroy();
+ mAutofillProxies.remove(sessionId);
+ }
+
+ private void handleOnUnbind() {
+ if (mAutofillProxies == null) {
+ if (DEBUG) Log.d(TAG, "onUnbind(): no proxy to destroy");
+ return;
+ }
+ final int size = mAutofillProxies.size();
+ if (DEBUG) Log.d(TAG, "onUnbind(): destroying " + size + " proxies");
+ for (int i = 0; i < size; i++) {
+ final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+ try {
+ proxy.destroy();
+ } catch (Exception e) {
+ Log.w(TAG, "error destroying " + proxy);
+ }
+ }
+ mAutofillProxies = null;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mAutofillProxies != null) {
+ final int size = mAutofillProxies.size();
+ pw.print("Number proxies: "); pw.println(size);
+ for (int i = 0; i < size; i++) {
+ final int sessionId = mAutofillProxies.keyAt(i);
+ final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+ pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":");
+ proxy.dump(" ", pw);
+ }
+ }
+ }
+
+ /** @hide */
+ static final class AutofillProxy {
+
+ static final int REPORT_EVENT_ON_SUCCESS = 1;
+ static final int REPORT_EVENT_UI_SHOWN = 2;
+ static final int REPORT_EVENT_UI_DESTROYED = 3;
+
+ @IntDef(prefix = { "REPORT_EVENT_" }, value = {
+ REPORT_EVENT_ON_SUCCESS,
+ REPORT_EVENT_UI_SHOWN,
+ REPORT_EVENT_UI_DESTROYED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ReportEvent{}
+
+
+ private final Object mLock = new Object();
+ private final IAugmentedAutofillManagerClient mClient;
+ private final int mSessionId;
+ private final IFillCallback mCallback;
+ public final int taskId;
+ public final ComponentName componentName;
+ @GuardedBy("mLock")
+ private AutofillId mFocusedId;
+ @GuardedBy("mLock")
+ private AutofillValue mFocusedValue;
+
+ // Objects used to log metrics
+ private final long mRequestTime;
+ private long mOnSuccessTime;
+ private long mUiFirstShownTime;
+ private long mUiFirstDestroyedTime;
+
+ @GuardedBy("mLock")
+ private SystemPopupPresentationParams mSmartSuggestion;
+
+ @GuardedBy("mLock")
+ private FillWindow mFillWindow;
+
+ private AutofillProxy(int sessionId, @NonNull IBinder client, int taskId,
+ @NonNull ComponentName componentName, @NonNull AutofillId focusedId,
+ @Nullable AutofillValue focusedValue, long requestTime,
+ @NonNull IFillCallback callback) {
+ mSessionId = sessionId;
+ mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
+ mCallback = callback;
+ this.taskId = taskId;
+ this.componentName = componentName;
+ this.mFocusedId = focusedId;
+ this.mFocusedValue = focusedValue;
+ this.mRequestTime = requestTime;
+ // TODO(b/111330312): linkToDeath
+ }
+
+ @NonNull
+ public SystemPopupPresentationParams getSmartSuggestionParams() {
+ synchronized (mLock) {
+ if (mSmartSuggestion != null) {
+ return mSmartSuggestion;
+ }
+ Rect rect;
+ try {
+ rect = mClient.getViewCoordinates(mFocusedId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not get coordinates for " + mFocusedId);
+ return null;
+ }
+ if (rect == null) {
+ if (DEBUG) Log.d(TAG, "getViewCoordinates(" + mFocusedId + ") returned null");
+ return null;
+ }
+ mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
+ return mSmartSuggestion;
+ }
+ }
+
+ public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs)
+ throws RemoteException {
+ final int size = pairs.size();
+ final List<AutofillId> ids = new ArrayList<>(size);
+ final List<AutofillValue> values = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ final Pair<AutofillId, AutofillValue> pair = pairs.get(i);
+ ids.add(pair.first);
+ values.add(pair.second);
+ }
+ mClient.autofill(mSessionId, ids, values);
+ }
+
+ public void setFillWindow(@NonNull FillWindow fillWindow) {
+ synchronized (mLock) {
+ mFillWindow = fillWindow;
+ }
+ }
+
+ public FillWindow getFillWindow() {
+ synchronized (mLock) {
+ return mFillWindow;
+ }
+ }
+
+ private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
+ synchronized (mLock) {
+ // TODO(b/111330312): should we close the popupwindow if the focused id changed?
+ mFocusedId = focusedId;
+ mFocusedValue = focusedValue;
+ }
+ }
+
+ @NonNull
+ public AutofillId getFocusedId() {
+ synchronized (mLock) {
+ return mFocusedId;
+ }
+ }
+
+ @NonNull
+ public AutofillValue getFocusedValue() {
+ synchronized (mLock) {
+ return mFocusedValue;
+ }
+ }
+
+ // Used (mostly) for metrics.
+ public void report(@ReportEvent int event) {
+ switch (event) {
+ case REPORT_EVENT_ON_SUCCESS:
+ if (mOnSuccessTime == 0) {
+ mOnSuccessTime = SystemClock.elapsedRealtime();
+ if (DEBUG) {
+ Slog.d(TAG, "Service responsed in "
+ + TimeUtils.formatDuration(mOnSuccessTime - mRequestTime));
+ }
+ }
+ try {
+ mCallback.onSuccess();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error reporting success: " + e);
+ }
+ break;
+ case REPORT_EVENT_UI_SHOWN:
+ if (mUiFirstShownTime == 0) {
+ mUiFirstShownTime = SystemClock.elapsedRealtime();
+ if (DEBUG) {
+ Slog.d(TAG, "UI shown in "
+ + TimeUtils.formatDuration(mUiFirstShownTime - mRequestTime));
+ }
+ }
+ break;
+ case REPORT_EVENT_UI_DESTROYED:
+ if (mUiFirstDestroyedTime == 0) {
+ mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
+ if (DEBUG) {
+ Slog.d(TAG, "UI destroyed in "
+ + TimeUtils.formatDuration(
+ mUiFirstDestroyedTime - mRequestTime));
+ }
+ }
+ break;
+ default:
+ Slog.w(TAG, "invalid event reported: " + event);
+ }
+ // TODO(b/111330312): log metrics as well
+ }
+
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("sessionId: "); pw.println(mSessionId);
+ pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
+ pw.print(prefix); pw.print("component: ");
+ pw.println(componentName.flattenToShortString());
+ pw.print(prefix); pw.print("focusedId: "); pw.println(mFocusedId);
+ if (mFocusedValue != null) {
+ pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
+ }
+ pw.print(prefix); pw.print("client: "); pw.println(mClient);
+ final String prefix2 = prefix + " ";
+ if (mFillWindow != null) {
+ pw.print(prefix); pw.println("window:");
+ mFillWindow.dump(prefix2, pw);
+ }
+ if (mSmartSuggestion != null) {
+ pw.print(prefix); pw.println("smartSuggestion:");
+ mSmartSuggestion.dump(prefix2, pw);
+ }
+ if (mOnSuccessTime > 0) {
+ final long responseTime = mOnSuccessTime - mRequestTime;
+ pw.print(prefix); pw.print("response time: ");
+ TimeUtils.formatDuration(responseTime, pw); pw.println();
+ }
+
+ if (mUiFirstShownTime > 0) {
+ final long uiRenderingTime = mUiFirstShownTime - mRequestTime;
+ pw.print(prefix); pw.print("UI rendering time: ");
+ TimeUtils.formatDuration(uiRenderingTime, pw); pw.println();
+ }
+
+ if (mUiFirstDestroyedTime > 0) {
+ final long uiTotalTime = mUiFirstDestroyedTime - mRequestTime;
+ pw.print(prefix); pw.print("UI life time: ");
+ TimeUtils.formatDuration(uiTotalTime, pw); pw.println();
+ }
+ }
+
+ private void destroy() {
+ synchronized (mLock) {
+ if (mFillWindow != null) {
+ if (DEBUG) Log.d(TAG, "destroying window");
+ mFillWindow.destroy();
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
similarity index 69%
rename from core/java/android/service/intelligence/FillCallback.java
rename to core/java/android/service/autofill/augmented/FillCallback.java
index af2da79..620ec59 100644
--- a/core/java/android/service/intelligence/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -13,10 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.autofill.augmented;
+import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+import android.util.Log;
/**
* Callback used to indicate at {@link FillRequest} has been fulfilled.
@@ -26,7 +31,13 @@
@SystemApi
public final class FillCallback {
- FillCallback() {}
+ private static final String TAG = FillCallback.class.getSimpleName();
+
+ private final AutofillProxy mProxy;
+
+ FillCallback(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
/**
* Sets the response associated with the request.
@@ -35,6 +46,11 @@
* could not provide autofill for the request.
*/
public void onSuccess(@Nullable FillResponse response) {
+ if (DEBUG) Log.d(TAG, "onSuccess(): " + response);
+
+ mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS);
+ if (response == null) return;
+
final FillWindow fillWindow = response.getFillWindow();
if (fillWindow != null) {
fillWindow.show();
diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
similarity index 90%
rename from core/java/android/service/intelligence/FillController.java
rename to core/java/android/service/autofill/augmented/FillController.java
index 4a9c85d..e65cf47 100644
--- a/core/java/android/service/intelligence/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.autofill.augmented;
-import static android.service.intelligence.SmartSuggestionsService.DEBUG;
+import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
-import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.util.Log;
import android.util.Pair;
import android.view.autofill.AutofillId;
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
similarity index 62%
rename from core/java/android/service/intelligence/FillRequest.java
rename to core/java/android/service/autofill/augmented/FillRequest.java
index f68db9d..57d2cc8 100644
--- a/core/java/android/service/intelligence/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -13,19 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.autofill.augmented;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
+import android.content.ComponentName;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
/**
* Represents a request to augment-fill an activity.
* @hide
*/
@SystemApi
+// TODO(b/111330312): pass a requestId and/or sessionId
public final class FillRequest {
final AutofillProxy mProxy;
@@ -36,11 +39,18 @@
}
/**
- * Gets the session associated with this request.
+ * Gets the task of the activity associated with this request.
+ */
+ public int getTaskId() {
+ return mProxy.taskId;
+ }
+
+ /**
+ * Gets the name of the activity associated with this request.
*/
@NonNull
- public InteractionSessionId getSessionId() {
- return mProxy.sessionId;
+ public ComponentName getActivityComponent() {
+ return mProxy.componentName;
}
/**
@@ -48,7 +58,15 @@
*/
@NonNull
public AutofillId getFocusedId() {
- return mProxy.focusedId;
+ return mProxy.getFocusedId();
+ }
+
+ /**
+ * Gets the current value of the field that triggered the request.
+ */
+ @NonNull
+ public AutofillValue getFocusedValue() {
+ return mProxy.getFocusedValue();
}
/**
@@ -63,6 +81,7 @@
@Override
public String toString() {
- return "FillRequest[id=" + mProxy.focusedId + "]";
+ return "FillRequest[act=" + getActivityComponent().flattenToShortString()
+ + ", id=" + mProxy.getFocusedId() + "]";
}
}
diff --git a/core/java/android/service/intelligence/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
similarity index 96%
rename from core/java/android/service/intelligence/FillResponse.java
rename to core/java/android/service/autofill/augmented/FillResponse.java
index 860c027..1ecfab4 100644
--- a/core/java/android/service/intelligence/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.autofill.augmented;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,7 +50,7 @@
* @hide
*/
@SystemApi
- public static class Builder {
+ public static final class Builder {
private FillWindow mFillWindow;
@@ -102,6 +102,8 @@
// TODO(b/111330312): add methods to disable app / activity, either here or on manager
}
+ // TODO(b/111330312): implement to String
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
similarity index 75%
rename from core/java/android/service/intelligence/FillWindow.java
rename to core/java/android/service/autofill/augmented/FillWindow.java
index 309f6a1..27df845 100644
--- a/core/java/android/service/intelligence/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -13,18 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.autofill.augmented;
-import static android.service.intelligence.SmartSuggestionsService.DEBUG;
+import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Dialog;
import android.graphics.Rect;
-import android.service.intelligence.PresentationParams.Area;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+import android.service.autofill.augmented.PresentationParams.Area;
import android.util.Log;
import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -33,6 +35,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import dalvik.system.CloseGuard;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,7 +61,7 @@
* @hide
*/
@SystemApi
-public final class FillWindow {
+public final class FillWindow implements AutoCloseable {
private static final String TAG = "FillWindow";
/** Indicates the data being shown is a physical address */
@@ -73,6 +77,7 @@
@interface Flags{}
private final Object mLock = new Object();
+ private final CloseGuard mCloseGuard = CloseGuard.get();
@GuardedBy("mLock")
private Dialog mDialog;
@@ -80,6 +85,8 @@
@GuardedBy("mLock")
private boolean mDestroyed;
+ private AutofillProxy mProxy;
+
/**
* Updates the content of the window.
*
@@ -123,6 +130,8 @@
synchronized (mLock) {
checkNotDestroyedLocked();
+ mProxy = area.proxy;
+
// TODO(b/111330312): once we have the SurfaceControl approach, we should update the
// window instead of destroying. In fact, it might be better to allocate a full window
// initially, which is transparent (and let touches get through) everywhere but in the
@@ -132,9 +141,24 @@
// TODO(b/111330312): make sure all touch events are handled, window is always closed,
// etc.
- mDialog = new Dialog(rootView.getContext());
+ mDialog = new Dialog(rootView.getContext()) {
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ FillWindow.this.destroy();
+ }
+ return false;
+ }
+ };
+ mCloseGuard.open("destroy");
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ // Makes sure touch outside the dialog is received by the window behind the dialog.
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+ // Makes sure the touch outside the dialog is received by the dialog to dismiss it.
+ window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ // Makes sure keyboard shows up.
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
final int height = rect.bottom - rect.top;
final int width = rect.right - rect.left;
@@ -156,7 +180,7 @@
Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
}
- area.proxy.setFillWindow(this);
+ mProxy.setFillWindow(this);
return true;
}
}
@@ -173,6 +197,9 @@
}
mDialog.show();
+ if (mProxy != null) {
+ mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
+ }
}
}
@@ -182,15 +209,29 @@
* <p>Once destroyed, this window cannot be used anymore
*/
public void destroy() {
- if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed);
+ if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog);
synchronized (this) {
- if (mDestroyed) return;
+ if (mDestroyed || mDialog == null) return;
- if (mDialog != null) {
- mDialog.dismiss();
- mDialog = null;
+ mDialog.dismiss();
+ mDialog = null;
+ if (mProxy != null) {
+ mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
}
+ mCloseGuard.close();
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ destroy();
+ } finally {
+ super.finalize();
}
}
@@ -212,4 +253,10 @@
}
}
}
+
+ /** @hide */
+ @Override
+ public void close() throws Exception {
+ destroy();
+ }
}
diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
new file mode 100644
index 0000000..b3ac2da
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill.augmented;
+
+import android.content.ComponentName;
+import android.os.IBinder;
+
+import android.service.autofill.augmented.IFillCallback;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import java.util.List;
+
+/**
+ * Interface from the system to an Augmented Autofill service.
+ *
+ * @hide
+ */
+oneway interface IAugmentedAutofillService {
+
+ void onFillRequest(int sessionId, in IBinder autofillManagerClient, int taskId,
+ in ComponentName activityComponent, in AutofillId focusedId,
+ in AutofillValue focusedValue, long requestTime, in IFillCallback callback);
+
+ void onDestroyFillWindowRequest(int sessionId);
+}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
new file mode 100644
index 0000000..dac7590
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill.augmented;
+
+import android.os.ICancellationSignal;
+
+/**
+ * Interface to receive the result of an autofill request.
+ *
+ * @hide
+ */
+interface IFillCallback {
+ // TODO(b/111330312): add cancellation (after we have CTS tests, so we can test it)
+// void onCancellable(in ICancellationSignal cancellation);
+ // TODO(b/111330312): might need to pass the response (once IME implements Smart Suggestions)
+ void onSuccess();
+}
diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
similarity index 95%
rename from core/java/android/service/intelligence/PresentationParams.java
rename to core/java/android/service/autofill/augmented/PresentationParams.java
index 9530309..0124ecc 100644
--- a/core/java/android/service/intelligence/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.autofill.augmented;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Rect;
-import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.util.DebugUtils;
import android.view.View;
@@ -174,11 +174,12 @@
*
* @param bounds boundaries relative to this Area.
*
- * @throws {@link IllegalArgumentException} if the {@code bounds} is not fully-contained
- * inside this full Area.
- *
* @return new subarea, or {@code null} if the Smart Suggestion host does not support such
* subaarea.
+ *
+ * @throws IllegalArgumentException if the {@code bounds} is not fully-contained inside this
+ * full Area.
+ *
*/
@Nullable
public Area getSubArea(@NonNull Rect bounds) {
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index e930f40..568ca0f 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -71,10 +71,8 @@
* @param gid2 group id level 2
* @param carrierid carrier unique identifier {@link TelephonyManager#getSimCarrierId()}, used
* to uniquely identify the carrier and look up the carrier configurations.
- * @param preciseCarrierId precise carrier identifier {@link TelephonyManager#getSimPreciseCarrierId()}
- * @hide
- *
- * TODO: expose this to public API
+ * @param preciseCarrierId precise carrier identifier
+ * {@link TelephonyManager#getSimPreciseCarrierId()}
*/
public CarrierIdentifier(String mcc, String mnc, @Nullable String spn,
@Nullable String imsi, @Nullable String gid1, @Nullable String gid2,
@@ -155,16 +153,16 @@
}
/**
- * Get the carrier id {@link TelephonyManager#getSimCarrierId() }
- * @hide
+ * Returns the carrier id.
+ * @see TelephonyManager#getSimCarrierId()
*/
public int getCarrierId() {
return mCarrierId;
}
/**
- * Get the precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()}
- * @hide
+ * Returns the precise carrier id.
+ * @see TelephonyManager#getSimPreciseCarrierId()
*/
public int getPreciseCarrierId() {
return mPreciseCarrierId;
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.aidl
similarity index 94%
rename from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
rename to core/java/android/service/contentcapture/ContentCaptureEventsRequest.aidl
index 23d607d..c032cfd 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.contentcapture;
parcelable ContentCaptureEventsRequest;
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
similarity index 95%
rename from core/java/android/service/intelligence/ContentCaptureEventsRequest.java
rename to core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index bc5b92b..df58f52 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.contentcapture;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.view.intelligence.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureEvent;
import java.util.List;
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
new file mode 100644
index 0000000..3dfeede
--- /dev/null
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.contentcapture;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureEvent;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A service used to capture the content of the screen to provide contextual data in other areas of
+ * the system such as Autofill.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ContentCaptureService extends Service {
+
+ private static final String TAG = ContentCaptureService.class.getSimpleName();
+
+ // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+ static final boolean DEBUG = true;
+ static final boolean VERBOSE = false;
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.contentcapture.ContentCaptureService";
+
+ private Handler mHandler;
+
+ private final IContentCaptureService mInterface = new IContentCaptureService.Stub() {
+
+ @Override
+ public void onSessionLifecycle(InteractionContext context, String sessionId)
+ throws RemoteException {
+ if (context != null) {
+ mHandler.sendMessage(
+ obtainMessage(ContentCaptureService::handleOnCreateInteractionSession,
+ ContentCaptureService.this, context, sessionId));
+ } else {
+ mHandler.sendMessage(
+ obtainMessage(ContentCaptureService::handleOnDestroyInteractionSession,
+ ContentCaptureService.this, sessionId));
+ }
+ }
+
+ @Override
+ public void onContentCaptureEventsRequest(String sessionId,
+ ContentCaptureEventsRequest request) {
+ mHandler.sendMessage(
+ obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest,
+ ContentCaptureService.this, sessionId, request));
+
+ }
+
+ @Override
+ public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) {
+ mHandler.sendMessage(
+ obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
+ ContentCaptureService.this, sessionId, snapshotData));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ /** @hide */
+ @Override
+ public final IBinder onBind(Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+ /**
+ * Explicitly limits content capture to the given packages and activities.
+ *
+ * <p>When the whitelist is set, it overrides the values passed to
+ * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}
+ * and {@link #setPackageContentCaptureEnabled(String, boolean)}.
+ *
+ * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+ *
+ * <p>Useful when the service wants to restrict content capture to a category of apps, like
+ * chat apps. For example, if the service wants to support view captures on all activities of
+ * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
+ * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
+ * Arrays.asList(new ComponentName("ChatApp2", "act1"),
+ * new ComponentName("ChatApp2", "act2")));}
+ */
+ public final void setContentCaptureWhitelist(@Nullable List<String> packages,
+ @Nullable List<ComponentName> activities) {
+ //TODO(b/111276913): implement
+ }
+
+ /**
+ * Defines whether content capture should be enabled for activities with such
+ * {@link android.content.ComponentName}.
+ *
+ * <p>Useful to blacklist a particular activity.
+ */
+ public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
+ boolean enabled) {
+ //TODO(b/111276913): implement
+ }
+
+ /**
+ * Defines whether content capture should be enabled for activities of the app with such
+ * {@code packageName}.
+ *
+ * <p>Useful to blacklist any activity from a particular app.
+ */
+ public final void setPackageContentCaptureEnabled(@NonNull String packageName,
+ boolean enabled) {
+ //TODO(b/111276913): implement
+ }
+
+ /**
+ * Gets the activities where content capture was disabled by
+ * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
+ */
+ @NonNull
+ public final Set<ComponentName> getContentCaptureDisabledActivities() {
+ //TODO(b/111276913): implement
+ return null;
+ }
+
+ /**
+ * Gets the apps where content capture was disabled by
+ * {@link #setPackageContentCaptureEnabled(String, boolean)}.
+ */
+ @NonNull
+ public final Set<String> getContentCaptureDisabledPackages() {
+ //TODO(b/111276913): implement
+ return null;
+ }
+
+ /**
+ * Creates a new interaction session.
+ *
+ * @param context interaction context
+ * @param sessionId the session's Id
+ */
+ public void onCreateInteractionSession(@NonNull InteractionContext context,
+ @NonNull InteractionSessionId sessionId) {
+ if (VERBOSE) {
+ Log.v(TAG, "onCreateInteractionSession(id=" + sessionId + ", ctx=" + context + ")");
+ }
+ }
+
+ /**
+ * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
+ * session.
+ *
+ * @param sessionId the session's Id
+ * @param request the events
+ */
+ public abstract void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
+ @NonNull ContentCaptureEventsRequest request);
+
+ /**
+ * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
+ *
+ * @param sessionId the session's Id
+ * @param snapshotData the data
+ */
+ public void onActivitySnapshot(@NonNull InteractionSessionId sessionId,
+ @NonNull SnapshotData snapshotData) {}
+
+ /**
+ * Destroys the interaction session.
+ *
+ * @param sessionId the id of the session to destroy
+ */
+ public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {
+ if (VERBOSE) {
+ Log.v(TAG, "onDestroyInteractionSession(id=" + sessionId + ")");
+ }
+ }
+
+ //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
+ // so we don't need to create a temporary InteractionSessionId for each event.
+
+ private void handleOnCreateInteractionSession(@NonNull InteractionContext context,
+ @NonNull String sessionId) {
+ onCreateInteractionSession(context, new InteractionSessionId(sessionId));
+ }
+
+ private void handleOnContentCaptureEventsRequest(@NonNull String sessionId,
+ @NonNull ContentCaptureEventsRequest request) {
+ onContentCaptureEventsRequest(new InteractionSessionId(sessionId), request);
+ }
+
+ private void handleOnActivitySnapshot(@NonNull String sessionId,
+ @NonNull SnapshotData snapshotData) {
+ onActivitySnapshot(new InteractionSessionId(sessionId), snapshotData);
+ }
+
+ private void handleOnDestroyInteractionSession(@NonNull String sessionId) {
+ onDestroyInteractionSession(new InteractionSessionId(sessionId));
+ }
+}
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
new file mode 100644
index 0000000..29e9abb
--- /dev/null
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.contentcapture;
+
+import android.service.contentcapture.ContentCaptureEventsRequest;
+import android.service.contentcapture.InteractionContext;
+import android.service.contentcapture.SnapshotData;
+
+import java.util.List;
+
+/**
+ * Interface from the system to a Content Capture service.
+ *
+ * @hide
+ */
+oneway interface IContentCaptureService {
+
+ // Called when session is created (context not null) or destroyed (context null)
+ void onSessionLifecycle(in InteractionContext context, String sessionId);
+
+ void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request);
+
+ void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
+}
diff --git a/core/java/android/service/intelligence/InteractionContext.aidl b/core/java/android/service/contentcapture/InteractionContext.aidl
similarity index 93%
rename from core/java/android/service/intelligence/InteractionContext.aidl
rename to core/java/android/service/contentcapture/InteractionContext.aidl
index 4ce6aa4..982e095 100644
--- a/core/java/android/service/intelligence/InteractionContext.aidl
+++ b/core/java/android/service/contentcapture/InteractionContext.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.contentcapture;
parcelable InteractionContext;
diff --git a/core/java/android/service/intelligence/InteractionContext.java b/core/java/android/service/contentcapture/InteractionContext.java
similarity index 96%
rename from core/java/android/service/intelligence/InteractionContext.java
rename to core/java/android/service/contentcapture/InteractionContext.java
index 7f4283d..f1281ff 100644
--- a/core/java/android/service/intelligence/InteractionContext.java
+++ b/core/java/android/service/contentcapture/InteractionContext.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.contentcapture;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -37,7 +37,7 @@
/**
* Flag used to indicate that the app explicitly disabled content capture for the activity
* (using
- * {@link android.view.intelligence.ContentCaptureManager#setContentCaptureEnabled()}),
+ * {@link android.view.contentcapture.ContentCaptureManager#setContentCaptureEnabled(boolean)}),
* in which case the service will just receive activity-level events.
*/
public static final int FLAG_DISABLED_BY_APP = 0x1;
diff --git a/core/java/android/service/intelligence/InteractionSessionId.java b/core/java/android/service/contentcapture/InteractionSessionId.java
similarity index 90%
rename from core/java/android/service/intelligence/InteractionSessionId.java
rename to core/java/android/service/contentcapture/InteractionSessionId.java
index 667193b..8411947 100644
--- a/core/java/android/service/intelligence/InteractionSessionId.java
+++ b/core/java/android/service/contentcapture/InteractionSessionId.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.contentcapture;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -22,10 +22,12 @@
import android.os.Parcelable;
import java.io.PrintWriter;
-import java.util.UUID;
-// TODO(b/111276913): add javadocs / implement equals/hashcode/string
-/** @hide */
+/**
+ * Identifier for a Content Capture session.
+ *
+ * @hide
+ */
@SystemApi
public final class InteractionSessionId implements Parcelable {
@@ -34,15 +36,6 @@
/**
* Creates a new instance.
*
- * @hide
- */
- public InteractionSessionId() {
- this(UUID.randomUUID().toString());
- }
-
- /**
- * Creates a new instance.
- *
* @param value The internal value.
*
* @hide
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/core/java/android/service/contentcapture/SnapshotData.aidl
similarity index 93%
rename from core/java/android/service/intelligence/SnapshotData.aidl
rename to core/java/android/service/contentcapture/SnapshotData.aidl
index 31d1339..a00e852 100644
--- a/core/java/android/service/intelligence/SnapshotData.aidl
+++ b/core/java/android/service/contentcapture/SnapshotData.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.contentcapture;
parcelable SnapshotData;
diff --git a/core/java/android/service/intelligence/SnapshotData.java b/core/java/android/service/contentcapture/SnapshotData.java
similarity index 98%
rename from core/java/android/service/intelligence/SnapshotData.java
rename to core/java/android/service/contentcapture/SnapshotData.java
index b9310ea..bc2116a 100644
--- a/core/java/android/service/intelligence/SnapshotData.java
+++ b/core/java/android/service/contentcapture/SnapshotData.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.service.contentcapture;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
deleted file mode 100644
index d6b3107..0000000
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.intelligence;
-
-import android.os.IBinder;
-import android.service.intelligence.ContentCaptureEventsRequest;
-import android.service.intelligence.InteractionSessionId;
-import android.service.intelligence.InteractionContext;
-import android.service.intelligence.SnapshotData;
-
-import android.view.autofill.AutofillId;
-import android.view.intelligence.ContentCaptureEvent;
-
-import java.util.List;
-
-/**
- * Interface from the system to an intelligence service.
- *
- * @hide
- */
- // TODO(b/111276913): rename / update javadoc (once the final name is defined)
-oneway interface IIntelligenceService {
-
- // Called when session is created (context not null) or destroyed (context null)
- void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
-
- void onContentCaptureEventsRequest(in InteractionSessionId sessionId,
- in ContentCaptureEventsRequest request);
-
- void onActivitySnapshot(in InteractionSessionId sessionId,
- in SnapshotData snapshotData);
-
- void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient,
- int autofilSessionId, in AutofillId focusedId);
-
- void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId);
-}
diff --git a/core/java/android/service/intelligence/InteractionSessionId.aidl b/core/java/android/service/intelligence/InteractionSessionId.aidl
deleted file mode 100644
index a5392b6..0000000
--- a/core/java/android/service/intelligence/InteractionSessionId.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.intelligence;
-
-parcelable InteractionSessionId;
diff --git a/core/java/android/service/intelligence/SmartSuggestionsService.java b/core/java/android/service/intelligence/SmartSuggestionsService.java
deleted file mode 100644
index 0e29e70..0000000
--- a/core/java/android/service/intelligence/SmartSuggestionsService.java
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.service.intelligence;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.service.intelligence.PresentationParams.SystemPopupPresentationParams;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
-import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
-import android.view.autofill.IAugmentedAutofillManagerClient;
-import android.view.intelligence.ContentCaptureEvent;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A service used to capture the content of the screen to provide contextual data in other areas of
- * the system such as Autofill.
- *
- * @hide
- */
-@SystemApi
-public abstract class SmartSuggestionsService extends Service {
-
- private static final String TAG = "SmartSuggestionsService";
-
- // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
- static final boolean DEBUG = true;
- static final boolean VERBOSE = false;
-
- /**
- * The {@link Intent} that must be declared as handled by the service.
- * To be supported, the service must also require the
- * {@link android.Manifest.permission#BIND_SMART_SUGGESTIONS_SERVICE} permission so
- * that other applications can not abuse it.
- */
- public static final String SERVICE_INTERFACE =
- "android.service.intelligence.SmartSuggestionsService";
-
- private Handler mHandler;
-
- private ArrayMap<InteractionSessionId, AutofillProxy> mAutofillProxies;
-
- private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
-
- @Override
- public void onSessionLifecycle(InteractionContext context, InteractionSessionId sessionId)
- throws RemoteException {
- if (context != null) {
- mHandler.sendMessage(
- obtainMessage(SmartSuggestionsService::onCreateInteractionSession,
- SmartSuggestionsService.this, context, sessionId));
- } else {
- mHandler.sendMessage(
- obtainMessage(SmartSuggestionsService::onDestroyInteractionSession,
- SmartSuggestionsService.this, sessionId));
- }
- }
-
- @Override
- public void onContentCaptureEventsRequest(InteractionSessionId sessionId,
- ContentCaptureEventsRequest request) {
- mHandler.sendMessage(
- obtainMessage(SmartSuggestionsService::onContentCaptureEventsRequest,
- SmartSuggestionsService.this, sessionId, request));
-
- }
-
- @Override
- public void onActivitySnapshot(InteractionSessionId sessionId,
- SnapshotData snapshotData) {
- mHandler.sendMessage(
- obtainMessage(SmartSuggestionsService::onActivitySnapshot,
- SmartSuggestionsService.this, sessionId, snapshotData));
- }
-
- @Override
- public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
- int autofilSessionId, AutofillId focusedId) {
- mHandler.sendMessage(obtainMessage(SmartSuggestionsService::handleOnAutofillRequest,
- SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId));
- }
-
- @Override
- public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
- mHandler.sendMessage(
- obtainMessage(SmartSuggestionsService::handleOnDestroyAutofillWindowsRequest,
- SmartSuggestionsService.this, sessionId));
- }
- };
-
- @CallSuper
- @Override
- public void onCreate() {
- super.onCreate();
- mHandler = new Handler(Looper.getMainLooper(), null, true);
- }
-
- /** @hide */
- @Override
- public final IBinder onBind(Intent intent) {
- if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mInterface.asBinder();
- }
- Log.w(TAG, "Tried to bind to wrong intent: " + intent);
- return null;
- }
-
- /**
- * Explicitly limits content capture to the given packages and activities.
- *
- * <p>When the whitelist is set, it overrides the values passed to
- * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}
- * and {@link #setPackageContentCaptureEnabled(String, boolean)}.
- *
- * <p>To reset the whitelist, call it passing {@code null} to both arguments.
- *
- * <p>Useful when the service wants to restrict content capture to a category of apps, like
- * chat apps. For example, if the service wants to support view captures on all activities of
- * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
- * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
- * Arrays.asList(new ComponentName("ChatApp2", "act1"),
- * new ComponentName("ChatApp2", "act2")));}
- */
- public final void setContentCaptureWhitelist(@Nullable List<String> packages,
- @Nullable List<ComponentName> activities) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Defines whether content capture should be enabled for activities with such
- * {@link android.content.ComponentName}.
- *
- * <p>Useful to blacklist a particular activity.
- */
- public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
- boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Defines whether content capture should be enabled for activities of the app with such
- * {@code packageName}.
- *
- * <p>Useful to blacklist any activity from a particular app.
- */
- public final void setPackageContentCaptureEnabled(@NonNull String packageName,
- boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Gets the activities where content capture was disabled by
- * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
- */
- @NonNull
- public final Set<ComponentName> getContentCaptureDisabledActivities() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /**
- * Gets the apps where content capture was disabled by
- * {@link #setPackageContentCaptureEnabled(String, boolean)}.
- */
- @NonNull
- public final Set<String> getContentCaptureDisabledPackages() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /**
- * Creates a new interaction session.
- *
- * @param context interaction context
- * @param sessionId the session's Id
- */
- public void onCreateInteractionSession(@NonNull InteractionContext context,
- @NonNull InteractionSessionId sessionId) {
- if (VERBOSE) {
- Log.v(TAG, "onCreateInteractionSession(id=" + sessionId + ", ctx=" + context + ")");
- }
- }
-
- /**
- * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
- * session.
- *
- * @param sessionId the session's Id
- * @param request the events
- */
- // TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a
- // Request object so it can be extended
- public abstract void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
- @NonNull ContentCaptureEventsRequest request);
-
- private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
- @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
- if (mAutofillProxies == null) {
- mAutofillProxies = new ArrayMap<>();
- }
- AutofillProxy proxy = mAutofillProxies.get(sessionId);
- if (proxy == null) {
- proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId);
- mAutofillProxies.put(sessionId, proxy);
- } else {
- // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
- if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
- }
- // TODO(b/111330312): set cancellation signal
- final CancellationSignal cancellationSignal = null;
- onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal,
- new FillController(proxy), new FillCallback());
- }
-
- /**
- * Asks the service to handle an "augmented" autofill request.
- *
- * <p>This method is called when the "stantard" autofill service cannot handle a request, which
- * typically occurs when:
- * <ul>
- * <li>Service does not recognize what should be autofilled.
- * <li>Service does not have data to fill the request.
- * <li>Service blacklisted that app (or activity) for autofill.
- * <li>App disabled itself for autofill.
- * </ul>
- *
- * <p>Differently from the standard autofill workflow, on augmented autofill the service is
- * responsible to generate the autofill UI and request the Android system to autofill the
- * activity when the user taps an action in that UI (through the
- * {@link FillController#autofill(List)} method).
- *
- * <p>The service <b>MUST</b> call {@link
- * FillCallback#onSuccess(android.service.intelligence.FillResponse)} as soon as possible,
- * passing {@code null} when it cannot fulfill the request.
- *
- * @param sessionId the session's id
- * @param request the request to handle.
- * @param cancellationSignal signal for observing cancellation requests. The system will use
- * this to notify you that the fill result is no longer needed and you should stop
- * handling this fill request in order to save resources.
- * @param controller object used to interact with the autofill system.
- * @param callback object used to notify the result of the request. Service <b>must</b> call
- * {@link FillCallback#onSuccess(android.service.intelligence.FillResponse)}.
- */
- public void onFillRequest(@NonNull InteractionSessionId sessionId, @NonNull FillRequest request,
- @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller,
- @NonNull FillCallback callback) {
- }
-
- private void handleOnDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
- AutofillProxy proxy = null;
- if (mAutofillProxies != null) {
- proxy = mAutofillProxies.get(sessionId);
- }
- if (proxy == null) {
- // TODO(b/111330312): this might be fine, in which case we should logv it
- Log.w(TAG, "No proxy for session " + sessionId);
- return;
- }
- proxy.destroy();
- mAutofillProxies.remove(sessionId);
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mAutofillProxies != null) {
- final int size = mAutofillProxies.size();
- pw.print("Number proxies: "); pw.println(size);
- for (int i = 0; i < size; i++) {
- final InteractionSessionId sessionId = mAutofillProxies.keyAt(i);
- final AutofillProxy proxy = mAutofillProxies.valueAt(i);
- pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":");
- proxy.dump(" ", pw);
- }
- }
- }
-
- /**
- * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
- *
- * @param sessionId the session's Id
- * @param snapshotData the data
- */
- public void onActivitySnapshot(@NonNull InteractionSessionId sessionId,
- @NonNull SnapshotData snapshotData) {}
-
- /**
- * Destroys the interaction session.
- *
- * @param sessionId the id of the session to destroy
- */
- public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {
- if (VERBOSE) {
- Log.v(TAG, "onDestroyInteractionSession(id=" + sessionId + ")");
- }
- }
-
- /** @hide */
- static final class AutofillProxy {
- private final Object mLock = new Object();
- private final IAugmentedAutofillManagerClient mClient;
- private final int mAutofillSessionId;
- public final InteractionSessionId sessionId;
- public final AutofillId focusedId;
-
- @GuardedBy("mLock")
- private SystemPopupPresentationParams mSmartSuggestion;
-
- @GuardedBy("mLock")
- private FillWindow mFillWindow;
-
- private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client,
- int autofillSessionId, @NonNull AutofillId focusedId) {
- this.sessionId = sessionId;
- mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
- mAutofillSessionId = autofillSessionId;
- this.focusedId = focusedId;
- // TODO(b/111330312): linkToDeath
- }
-
- @NonNull
- public SystemPopupPresentationParams getSmartSuggestionParams() {
- synchronized (mLock) {
- if (mSmartSuggestion != null) {
- return mSmartSuggestion;
- }
- Rect rect;
- try {
- rect = mClient.getViewCoordinates(focusedId);
- } catch (RemoteException e) {
- Log.w(TAG, "Could not get coordinates for " + focusedId);
- return null;
- }
- if (rect == null) {
- if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
- return null;
- }
- mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
- return mSmartSuggestion;
- }
- }
-
- public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs)
- throws RemoteException {
- final int size = pairs.size();
- final List<AutofillId> ids = new ArrayList<>(size);
- final List<AutofillValue> values = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- final Pair<AutofillId, AutofillValue> pair = pairs.get(i);
- ids.add(pair.first);
- values.add(pair.second);
- }
- mClient.autofill(mAutofillSessionId, ids, values);
- }
-
- public void setFillWindow(@NonNull FillWindow fillWindow) {
- synchronized (mLock) {
- mFillWindow = fillWindow;
- }
- }
-
- public FillWindow getFillWindow() {
- synchronized (mLock) {
- return mFillWindow;
- }
- }
-
- public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId);
- pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
- pw.print(prefix); pw.print("client: "); pw.println(mClient);
- final String prefix2 = prefix + " ";
- if (mFillWindow != null) {
- pw.print(prefix); pw.println("window:");
- mFillWindow.dump(prefix2, pw);
- }
- if (mSmartSuggestion != null) {
- pw.print(prefix); pw.println("smartSuggestion:");
- mSmartSuggestion.dump(prefix2, pw);
- }
- }
-
- private void destroy() {
- synchronized (mLock) {
- if (mFillWindow != null) {
- if (DEBUG) Log.d(TAG, "destroying window");
- mFillWindow.destroy();
- }
- }
- }
- }
-}
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 518f8ed..de532b7 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -80,11 +80,18 @@
* Data type: int, one of importance values e.g.
* {@link android.app.NotificationManager#IMPORTANCE_MIN}.
*
- * If used from
- * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, it can
- * block a notification from appearing or silence it. If used from
- * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can visually
- * demote a notification.
+ * <p> If used from
+ * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, and
+ * received before the notification is posted, it can block a notification from appearing or
+ * silence it. Importance adjustments received too late from
+ * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)} will be
+ * ignored.
+ * </p>
+ * <p>If used from
+ * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can
+ * visually demote or cancel a notification, but use this with care if they notification was
+ * recently posted because the notification may already have made noise.
+ * </p>
*/
public static final String KEY_IMPORTANCE = "key_importance";
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 5a7a83f..af7e93e 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -17,7 +17,6 @@
package android.service.notification;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
@@ -35,7 +34,6 @@
*/
public final class Condition implements Parcelable {
- @SystemApi
public static final String SCHEME = "condition";
/** @hide */
@@ -59,14 +57,10 @@
*/
public static final int STATE_TRUE = 1;
- @SystemApi
public static final int STATE_UNKNOWN = 2;
- @SystemApi
public static final int STATE_ERROR = 3;
- @SystemApi
public static final int FLAG_RELEVANT_NOW = 1 << 0;
- @SystemApi
public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
/**
@@ -81,9 +75,7 @@
*/
public final String summary;
- @SystemApi
public final String line1;
- @SystemApi
public final String line2;
/**
@@ -94,9 +86,7 @@
@State
public final int state;
- @SystemApi
public final int flags;
- @SystemApi
public final int icon;
/**
@@ -108,7 +98,6 @@
this(id, summary, "", "", -1, state, FLAG_RELEVANT_ALWAYS);
}
- @SystemApi
public Condition(Uri id, String summary, String line1, String line2, int icon,
int state, int flags) {
if (id == null) throw new IllegalArgumentException("id is required");
@@ -177,7 +166,6 @@
proto.end(token);
}
- @SystemApi
public static String stateToString(int state) {
if (state == STATE_FALSE) return "STATE_FALSE";
if (state == STATE_TRUE) return "STATE_TRUE";
@@ -186,7 +174,6 @@
throw new IllegalArgumentException("state is invalid: " + state);
}
- @SystemApi
public static String relevanceToString(int flags) {
final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
@@ -219,7 +206,6 @@
return 0;
}
- @SystemApi
public Condition copy() {
final Parcel parcel = Parcel.obtain();
try {
@@ -231,14 +217,12 @@
}
}
- @SystemApi
public static Uri.Builder newId(Context context) {
return new Uri.Builder()
.scheme(Condition.SCHEME)
.authority(context.getPackageName());
}
- @SystemApi
public static boolean isValidId(Uri id, String pkg) {
return id != null && SCHEME.equals(id.getScheme()) && pkg.equals(id.getAuthority());
}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 6fc689a..5203c8f 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -17,7 +17,6 @@
package android.service.notification;
import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.INotificationManager;
@@ -107,7 +106,6 @@
*/
abstract public void onConnected();
- @SystemApi
public void onRequestConditions(int relevance) {}
/**
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index d7359f1..3b813c7 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -81,7 +81,6 @@
* {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
* that other applications can not abuse it.
*/
- @SystemApi
public static final String SERVICE_INTERFACE =
"android.service.textclassifier.TextClassifierService";
@@ -407,9 +406,7 @@
* Callbacks for TextClassifierService results.
*
* @param <T> the type of the result
- * @hide
*/
- @SystemApi
public interface Callback<T> {
/**
* Returns the result.
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index f9370a8..7e41878 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -377,6 +377,9 @@
* @param start the inclusive start offset of the target region in the text
* @param end the exclusive end offset of the target region in the text
* @param textDir the text direction
+ * @param computeHyphenation true if need to compute hyphenation, otherwise false
+ * @param computeLayout true if need to compute full layout, otherwise false.
+ * @param hint pass if you already have measured paragraph.
* @param recycle pass existing MeasuredParagraph if you want to recycle it.
*
* @return measured text
@@ -389,12 +392,18 @@
@NonNull TextDirectionHeuristic textDir,
boolean computeHyphenation,
boolean computeLayout,
+ @Nullable MeasuredParagraph hint,
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
- final MeasuredText.Builder builder = new MeasuredText.Builder(mt.mCopiedBuffer);
- builder.setComputeHyphenation(computeHyphenation);
- builder.setComputeLayout(computeLayout);
+ final MeasuredText.Builder builder;
+ if (hint == null) {
+ builder = new MeasuredText.Builder(mt.mCopiedBuffer)
+ .setComputeHyphenation(computeHyphenation)
+ .setComputeLayout(computeLayout);
+ } else {
+ builder = new MeasuredText.Builder(hint.mMeasuredText);
+ }
if (mt.mTextLength == 0) {
// Need to build empty native measured text for StaticLayout.
// TODO: Stop creating empty measured text for empty lines.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index b7ea012..08741d6 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -17,6 +17,7 @@
package android.text;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,6 +26,8 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
@@ -119,6 +122,16 @@
}
/**
+ * Builder constructor from existing params.
+ */
+ public Builder(@NonNull Params params) {
+ mPaint = params.mPaint;
+ mTextDir = params.mTextDir;
+ mBreakStrategy = params.mBreakStrategy;
+ mHyphenationFrequency = params.mHyphenationFrequency;
+ }
+
+ /**
* Set the line break strategy.
*
* The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
@@ -220,13 +233,41 @@
}
/** @hide */
- public boolean isSameTextMetricsInternal(@NonNull TextPaint paint,
+ @IntDef(value = { UNUSABLE, NEED_RECOMPUTE, USABLE })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CheckResultUsableResult {}
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is not
+ * compatible.
+ * @hide
+ */
+ public static final int UNUSABLE = 0;
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is not
+ * compatible but partially usable for creating new PrecomputedText.
+ * @hide
+ */
+ public static final int NEED_RECOMPUTE = 1;
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is
+ * compatible.
+ * @hide
+ */
+ public static final int USABLE = 2;
+
+ /** @hide */
+ public @CheckResultUsableResult int checkResultUsable(@NonNull TextPaint paint,
@NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
@Layout.HyphenationFrequency int frequency) {
- return mTextDir == textDir
- && mBreakStrategy == strategy
- && mHyphenationFrequency == frequency
- && mPaint.equalsForTextMeasurement(paint);
+ if (mBreakStrategy == strategy && mHyphenationFrequency == frequency
+ && mPaint.equalsForTextMeasurement(paint)) {
+ return mTextDir == textDir ? USABLE : NEED_RECOMPUTE;
+ } else {
+ return UNUSABLE;
+ }
}
/**
@@ -243,8 +284,8 @@
return false;
}
Params param = (Params) o;
- return isSameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
- param.mHyphenationFrequency);
+ return checkResultUsable(param.mPaint, param.mTextDir, param.mBreakStrategy,
+ param.mHyphenationFrequency) == Params.USABLE;
}
@Override
@@ -321,11 +362,55 @@
* @return A {@link PrecomputedText}
*/
public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
- ParagraphInfo[] paraInfo = createMeasuredParagraphs(
- text, params, 0, text.length(), true /* computeLayout */);
+ ParagraphInfo[] paraInfo = null;
+ if (text instanceof PrecomputedText) {
+ final PrecomputedText hintPct = (PrecomputedText) text;
+ final PrecomputedText.Params hintParams = hintPct.getParams();
+ final @Params.CheckResultUsableResult int checkResult =
+ hintParams.checkResultUsable(params.mPaint, params.mTextDir,
+ params.mBreakStrategy, params.mHyphenationFrequency);
+ switch (checkResult) {
+ case Params.USABLE:
+ return hintPct;
+ case Params.NEED_RECOMPUTE:
+ // To be able to use PrecomputedText for new params, at least break strategy and
+ // hyphenation frequency must be the same.
+ if (params.getBreakStrategy() == hintParams.getBreakStrategy()
+ && params.getHyphenationFrequency()
+ == hintParams.getHyphenationFrequency()) {
+ paraInfo = createMeasuredParagraphsFromPrecomputedText(
+ hintPct, params, true /* compute layout */);
+ }
+ break;
+ case Params.UNUSABLE:
+ // Unable to use anything in PrecomputedText. Create PrecomputedText as the
+ // normal text input.
+ }
+
+ }
+ if (paraInfo == null) {
+ paraInfo = createMeasuredParagraphs(
+ text, params, 0, text.length(), true /* computeLayout */);
+ }
return new PrecomputedText(text, 0, text.length(), params, paraInfo);
}
+ private static ParagraphInfo[] createMeasuredParagraphsFromPrecomputedText(
+ @NonNull PrecomputedText pct, @NonNull Params params, boolean computeLayout) {
+ final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
+ && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
+ ArrayList<ParagraphInfo> result = new ArrayList<>();
+ for (int i = 0; i < pct.getParagraphCount(); ++i) {
+ final int paraStart = pct.getParagraphStart(i);
+ final int paraEnd = pct.getParagraphEnd(i);
+ result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
+ params.getTextPaint(), pct, paraStart, paraEnd, params.getTextDirection(),
+ needHyphenation, computeLayout, pct.getMeasuredParagraph(i),
+ null /* no recycle */)));
+ }
+ return result.toArray(new ParagraphInfo[result.size()]);
+ }
+
/** @hide */
public static ParagraphInfo[] createMeasuredParagraphs(
@NonNull CharSequence text, @NonNull Params params,
@@ -350,7 +435,8 @@
result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
- needHyphenation, computeLayout, null /* no recycle */)));
+ needHyphenation, computeLayout, null /* no hint */,
+ null /* no recycle */)));
}
return result.toArray(new ParagraphInfo[result.size()]);
}
@@ -434,12 +520,15 @@
* Returns true if the given TextPaint gives the same result of text layout for this text.
* @hide
*/
- public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
- @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
- return mStart == start
- && mEnd == end
- && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
+ public @Params.CheckResultUsableResult int checkResultUsable(@IntRange(from = 0) int start,
+ @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir,
+ @NonNull TextPaint paint, @Layout.BreakStrategy int strategy,
+ @Layout.HyphenationFrequency int frequency) {
+ if (mStart != start || mEnd != end) {
+ return Params.UNUSABLE;
+ } else {
+ return mParams.checkResultUsable(paint, textDir, strategy, frequency);
+ }
}
/** @hide */
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 8cb18b2..3d0c662 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -650,10 +650,26 @@
final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
if (source instanceof PrecomputedText) {
PrecomputedText precomputed = (PrecomputedText) source;
- if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
- b.mBreakStrategy, b.mHyphenationFrequency)) {
- // Some parameters are different from the ones when measured text is created.
- paragraphInfo = precomputed.getParagraphInfo();
+ final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
+ precomputed.checkResultUsable(bufStart, bufEnd, textDir, paint,
+ b.mBreakStrategy, b.mHyphenationFrequency);
+ switch (checkResult) {
+ case PrecomputedText.Params.UNUSABLE:
+ break;
+ case PrecomputedText.Params.NEED_RECOMPUTE:
+ final PrecomputedText.Params newParams =
+ new PrecomputedText.Params.Builder(paint)
+ .setBreakStrategy(b.mBreakStrategy)
+ .setHyphenationFrequency(b.mHyphenationFrequency)
+ .setTextDirection(textDir)
+ .build();
+ precomputed = PrecomputedText.create(precomputed, newParams);
+ paragraphInfo = precomputed.getParagraphInfo();
+ break;
+ case PrecomputedText.Params.USABLE:
+ // Some parameters are different from the ones when measured text is created.
+ paragraphInfo = precomputed.getParagraphInfo();
+ break;
}
}
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index be47320..433483f7 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -369,10 +369,7 @@
/**
* @return The color of the underline for that span, or 0 if there is no underline
- *
- * @hide
*/
- @UnsupportedAppUsage
public int getUnderlineColor() {
// The order here should match what is used in updateDrawState
final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5f348c4..ade7577 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,13 +42,15 @@
static {
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
- DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_dynamic_homepage", "true");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
- DEFAULT_FLAGS.put("settings_data_usage_v2", "true");
- DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
- DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
+ DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
+ DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+ DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
+ DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
+ DEFAULT_FLAGS.put("settings_wifi_sharing", "false");
+ DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SAFETY_HUB, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 3ee5f1f..33b3ff4f 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -18,6 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -225,7 +226,7 @@
private int mMinimumFlingVelocity;
private int mMaximumFlingVelocity;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
@@ -592,8 +593,8 @@
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
- mHandler.sendEmptyMessageAtTime(LONG_PRESS,
- mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
+ mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ + ViewConfiguration.getLongPressTimeout());
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
diff --git a/core/java/android/view/InputEventCompatProcessor.java b/core/java/android/view/InputEventCompatProcessor.java
new file mode 100644
index 0000000..ff8407a
--- /dev/null
+++ b/core/java/android/view/InputEventCompatProcessor.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+import android.os.Build;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Compatibility processor for InputEvents that allows events to be adjusted before and
+ * after it is sent to the application.
+ *
+ * {@hide}
+ */
+public class InputEventCompatProcessor {
+
+ protected Context mContext;
+ protected int mTargetSdkVersion;
+
+ /** List of events to be used to return the processed events */
+ private List<InputEvent> mProcessedEvents;
+
+ public InputEventCompatProcessor(Context context) {
+ mContext = context;
+ mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+ mProcessedEvents = new ArrayList<>();
+ }
+
+ /**
+ * Processes the InputEvent for compatibility before it is sent to the app, allowing for the
+ * generation of more than one event if necessary.
+ *
+ * @param e The InputEvent to process
+ * @return The list of adjusted events, or null if no adjustments are needed. Do not keep a
+ * reference to the output as the list is reused.
+ */
+ public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
+ if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
+ mProcessedEvents.clear();
+ MotionEvent motion = (MotionEvent) e;
+ final int mask =
+ MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
+ final int buttonState = motion.getButtonState();
+ final int compatButtonState = (buttonState & mask) >> 4;
+ if (compatButtonState != 0) {
+ motion.setButtonState(buttonState | compatButtonState);
+ }
+ mProcessedEvents.add(motion);
+ return mProcessedEvents;
+ }
+ return null;
+ }
+
+ /**
+ * Processes the InputEvent for compatibility before it is finished by calling
+ * InputEventReceiver#finishInputEvent().
+ *
+ * @param e The InputEvent to process
+ * @return The InputEvent to finish, or null if it should not be finished
+ */
+ public InputEvent processInputEventBeforeFinish(InputEvent e) {
+ // No changes needed
+ return e;
+ }
+}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index ba5340c..fb4f9c0 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -38,9 +38,14 @@
private final InsetsState mState = new InsetsState();
private final Rect mFrame = new Rect();
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
+ private final ViewRootImpl mViewRoot;
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+ public InsetsController(ViewRootImpl viewRoot) {
+ mViewRoot = viewRoot;
+ }
+
void onFrameChanged(Rect frame) {
mFrame.set(frame);
}
@@ -49,8 +54,14 @@
return mState;
}
- public void setState(InsetsState state) {
+ boolean onStateChanged(InsetsState state) {
+ if (mState.equals(state)) {
+ return false;
+ }
mState.set(state);
+ applyLocalVisibilityOverride();
+ mViewRoot.notifyInsetsChanged();
+ return true;
}
/**
@@ -105,17 +116,28 @@
}
}
+ private void applyLocalVisibilityOverride() {
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
+ controller.applyLocalVisibilityOverride();
+ }
+ }
+
@VisibleForTesting
public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetType int type) {
InsetsSourceConsumer controller = mSourceConsumers.get(type);
if (controller != null) {
return controller;
}
- controller = new InsetsSourceConsumer(type, mState, Transaction::new);
+ controller = new InsetsSourceConsumer(type, mState, Transaction::new, this);
mSourceConsumers.put(type, controller);
return controller;
}
+ void notifyVisibilityChanged() {
+ mViewRoot.notifyInsetsChanged();
+ }
+
void dump(String prefix, PrintWriter pw) {
pw.println(prefix); pw.println("InsetsController:");
mState.dump(prefix + " ", pw);
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index e74aa8d..ec85c4c 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -33,27 +33,31 @@
private final Supplier<Transaction> mTransactionSupplier;
private final @InternalInsetType int mType;
private final InsetsState mState;
- private @Nullable InsetsSourceControl mControl;
+ private final InsetsController mController;
+ private @Nullable InsetsSourceControl mSourceControl;
private boolean mHidden;
public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
- Supplier<Transaction> transactionSupplier) {
+ Supplier<Transaction> transactionSupplier, InsetsController controller) {
mType = type;
mState = state;
mTransactionSupplier = transactionSupplier;
+ mController = controller;
}
public void setControl(@Nullable InsetsSourceControl control) {
- if (mControl == control) {
+ if (mSourceControl == control) {
return;
}
- mControl = control;
+ mSourceControl = control;
applyHiddenToControl();
+ applyLocalVisibilityOverride();
+ mController.notifyVisibilityChanged();
}
@VisibleForTesting
public InsetsSourceControl getControl() {
- return mControl;
+ return mSourceControl;
}
int getType() {
@@ -70,25 +74,36 @@
setHidden(true);
}
+ void applyLocalVisibilityOverride() {
+
+ // If we don't have control, we are not able to change the visibility.
+ if (mSourceControl == null) {
+ return;
+ }
+ mState.getSource(mType).setVisible(!mHidden);
+ }
+
private void setHidden(boolean hidden) {
if (mHidden == hidden) {
return;
}
mHidden = hidden;
applyHiddenToControl();
+ applyLocalVisibilityOverride();
+ mController.notifyVisibilityChanged();
}
private void applyHiddenToControl() {
- if (mControl == null) {
+ if (mSourceControl == null) {
return;
}
// TODO: Animation
final Transaction t = mTransactionSupplier.get();
if (mHidden) {
- t.hide(mControl.getLeash());
+ t.hide(mSourceControl.getLeash());
} else {
- t.show(mControl.getLeash());
+ t.show(mSourceControl.getLeash());
}
t.apply();
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 8b39cc7..9cced4e 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1803,6 +1803,28 @@
}
/**
+ * Returns whether this key will be sent to the
+ * {@link android.media.session.MediaSession.Callback} if not handled.
+ */
+ public static final boolean isMediaSessionKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Returns true if the specified keycode is a gamepad button.
* @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
*/
@@ -1861,31 +1883,6 @@
}
}
- /**
- * Returns whether this key can be handled by
- * {@link android.media.session.MediaSession.Callback}.
- *
- * @hide
- */
- public static final boolean isMediaSessionKey(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_MUTE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_STOP:
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- case KeyEvent.KEYCODE_MEDIA_RECORD:
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
- return true;
- }
- return false;
- }
-
-
/** Is this a system key? System keys can not be used for menu shortcuts.
* @hide
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab01085..a006e5d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -36,6 +36,8 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -129,6 +131,12 @@
int width, int height);
private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
IBinder displayToken);
+ private static native DisplayedContentSamplingAttributes
+ nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
+ private static native boolean nativeSetDisplayedContentSamplingEnabled(IBinder displayToken,
+ boolean enable, int componentMask, int maxFrames);
+ private static native DisplayedContentSample nativeGetDisplayedContentSample(
+ IBinder displayToken, long numFrames, long timestamp);
private static native int nativeGetActiveConfig(IBinder displayToken);
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
@@ -1164,6 +1172,45 @@
return nativeGetActiveConfig(displayToken);
}
+ /**
+ * @hide
+ */
+ public static DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+ IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetDisplayedContentSamplingAttributes(displayToken);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean setDisplayedContentSamplingEnabled(
+ IBinder displayToken, boolean enable, int componentMask, int maxFrames) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ final int maxColorComponents = 4;
+ if ((componentMask >> maxColorComponents) != 0) {
+ throw new IllegalArgumentException("invalid componentMask when enabling sampling");
+ }
+ return nativeSetDisplayedContentSamplingEnabled(
+ displayToken, enable, componentMask, maxFrames);
+ }
+
+ /**
+ * @hide
+ */
+ public static DisplayedContentSample getDisplayedContentSample(
+ IBinder displayToken, long maxFrames, long timestamp) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetDisplayedContentSample(displayToken, maxFrames, timestamp);
+ }
+
+
public static boolean setActiveConfig(IBinder displayToken, int id) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index bf1a005..34d076f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -299,6 +299,8 @@
private boolean mEnabled;
private boolean mRequested = true;
+ private FrameDrawingCallback mNextRtFrameCallback;
+
ThreadedRenderer(Context context, boolean translucent, String name) {
super();
setName(name);
@@ -432,6 +434,17 @@
}
/**
+ * Registers a callback to be executed when the next frame is being drawn on RenderThread. This
+ * callback will be executed on a RenderThread worker thread, and only used for the next frame
+ * and thus it will only fire once.
+ *
+ * @param callback The callback to register.
+ */
+ void registerRtFrameCallback(FrameDrawingCallback callback) {
+ mNextRtFrameCallback = callback;
+ }
+
+ /**
* Destroys all hardware rendering resources associated with the specified
* view hierarchy.
*
@@ -562,6 +575,15 @@
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
updateViewTreeDisplayList(view);
+ // Consume and set the frame callback after we dispatch draw to the view above, but before
+ // onPostDraw below which may reset the callback for the next frame. This ensures that
+ // updates to the frame callback during scroll handling will also apply in this frame.
+ final FrameDrawingCallback callback = mNextRtFrameCallback;
+ mNextRtFrameCallback = null;
+ if (callback != null) {
+ setFrameCallback(callback);
+ }
+
if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
RecordingCanvas canvas = mRootNode.startRecording(mSurfaceWidth, mSurfaceHeight);
try {
@@ -619,10 +641,8 @@
*
* @param view The view to draw.
* @param attachInfo AttachInfo tied to the specified view.
- * @param callbacks Callbacks invoked when drawing happens.
*/
- void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks,
- FrameDrawingCallback frameDrawingCallback) {
+ void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
choreographer.mFrameInfo.markDrawStart();
@@ -642,9 +662,6 @@
attachInfo.mPendingAnimatingRenderNodes = null;
}
- if (frameDrawingCallback != null) {
- setFrameCallback(frameDrawingCallback);
- }
int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
setEnabled(false);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4b9cbff..4297efb7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -109,10 +109,13 @@
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.view.contentcapture.ContentCaptureManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.intelligence.ContentCaptureManager;
+import android.view.inspector.InspectableProperty;
+import android.view.inspector.InspectableProperty.EnumMap;
+import android.view.inspector.InspectableProperty.FlagMap;
import android.widget.Checkable;
import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
@@ -4895,6 +4898,15 @@
*/
public static final int LAYER_TYPE_HARDWARE = 2;
+ /** @hide */
+ @IntDef(prefix = { "LAYER_TYPE_" }, value = {
+ LAYER_TYPE_NONE,
+ LAYER_TYPE_SOFTWARE,
+ LAYER_TYPE_HARDWARE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LayerType {}
+
@ViewDebug.ExportedProperty(category = "drawing", mapping = {
@ViewDebug.IntToString(from = LAYER_TYPE_NONE, to = "NONE"),
@ViewDebug.IntToString(from = LAYER_TYPE_SOFTWARE, to = "SOFTWARE"),
@@ -6516,6 +6528,15 @@
*
* @return a bitmask representing the enabled scroll indicators
*/
+ @InspectableProperty(flagMapping = {
+ @FlagMap(target = SCROLL_INDICATORS_NONE, mask = 0xffff_ffff, name = "none"),
+ @FlagMap(target = SCROLL_INDICATOR_TOP, name = "top"),
+ @FlagMap(target = SCROLL_INDICATOR_BOTTOM, name = "bottom"),
+ @FlagMap(target = SCROLL_INDICATOR_LEFT, name = "left"),
+ @FlagMap(target = SCROLL_INDICATOR_RIGHT, name = "right"),
+ @FlagMap(target = SCROLL_INDICATOR_START, name = "start"),
+ @FlagMap(target = SCROLL_INDICATOR_END, name = "end")
+ })
@ScrollIndicators
public int getScrollIndicators() {
return (mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK)
@@ -7582,7 +7603,9 @@
*
* {@see #setAccessibilityPaneTitle}.
*/
- @Nullable public CharSequence getAccessibilityPaneTitle() {
+ @InspectableProperty
+ @Nullable
+ public CharSequence getAccessibilityPaneTitle() {
return mAccessibilityPaneTitle;
}
@@ -8580,6 +8603,7 @@
* @attr ref android.R.styleable#View_autofillHints
*/
@ViewDebug.ExportedProperty()
+ @InspectableProperty
@Nullable public String[] getAutofillHints() {
return mAutofillHints;
}
@@ -8624,6 +8648,15 @@
to = "yesExcludeDescendants"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
to = "noExcludeDescendants")})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = IMPORTANT_FOR_AUTOFILL_AUTO, name = "auto"),
+ @EnumMap(value = IMPORTANT_FOR_AUTOFILL_YES, name = "yes"),
+ @EnumMap(value = IMPORTANT_FOR_AUTOFILL_NO, name = "no"),
+ @EnumMap(value = IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
+ name = "yesExcludeDescendants"),
+ @EnumMap(value = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
+ name = "noExcludeDescendants"),
+ })
public @AutofillImportance int getImportantForAutofill() {
return (mPrivateFlags3
& PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK) >> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
@@ -8808,6 +8841,15 @@
to = "yesExcludeDescendants"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
to = "noExcludeDescendants")})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"),
+ @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"),
+ @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"),
+ @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
+ name = "yesExcludeDescendants"),
+ @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
+ name = "noExcludeDescendants"),
+ })
public @ContentCaptureImportance int getImportantForContentCapture() {
// NOTE: the important for content capture values were the first flags added and are set in
// the rightmost position, so we don't need to shift them
@@ -9619,6 +9661,7 @@
* @attr ref android.R.styleable#View_contentDescription
*/
@ViewDebug.ExportedProperty(category = "accessibility")
+ @InspectableProperty
public CharSequence getContentDescription() {
return mContentDescription;
}
@@ -9699,6 +9742,7 @@
*
* @see #setAccessibilityTraversalBefore(int)
*/
+ @InspectableProperty
public int getAccessibilityTraversalBefore() {
return mAccessibilityTraversalBeforeId;
}
@@ -9743,6 +9787,7 @@
*
* @see #setAccessibilityTraversalAfter(int)
*/
+ @InspectableProperty
public int getAccessibilityTraversalAfter() {
return mAccessibilityTraversalAfterId;
}
@@ -9754,6 +9799,7 @@
* @return The labeled view id.
*/
@ViewDebug.ExportedProperty(category = "accessibility")
+ @InspectableProperty
public int getLabelFor() {
return mLabelForId;
}
@@ -9817,6 +9863,7 @@
* @return True if this view has focus, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty(hasAttributeId = false)
public boolean isFocused() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
@@ -9841,6 +9888,7 @@
*
* @attr ref android.R.styleable#View_isScrollContainer
*/
+ @InspectableProperty(name = "isScrollContainer")
public boolean isScrollContainer() {
return (mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0;
}
@@ -9896,6 +9944,11 @@
*/
@Deprecated
@DrawingCacheQuality
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = DRAWING_CACHE_QUALITY_LOW, name = "low"),
+ @EnumMap(value = DRAWING_CACHE_QUALITY_HIGH, name = "high"),
+ @EnumMap(value = DRAWING_CACHE_QUALITY_AUTO, name = "auto")
+ })
public int getDrawingCacheQuality() {
return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
}
@@ -9941,6 +9994,7 @@
*
* @attr ref android.R.styleable#View_keepScreenOn
*/
+ @InspectableProperty
public boolean getKeepScreenOn() {
return (mViewFlags & KEEP_SCREEN_ON) != 0;
}
@@ -9965,6 +10019,7 @@
*
* @attr ref android.R.styleable#View_nextFocusLeft
*/
+ @InspectableProperty(name = "nextFocusLeft")
public int getNextFocusLeftId() {
return mNextFocusLeftId;
}
@@ -9986,6 +10041,7 @@
*
* @attr ref android.R.styleable#View_nextFocusRight
*/
+ @InspectableProperty(name = "nextFocusRight")
public int getNextFocusRightId() {
return mNextFocusRightId;
}
@@ -10007,6 +10063,7 @@
*
* @attr ref android.R.styleable#View_nextFocusUp
*/
+ @InspectableProperty(name = "nextFocusUp")
public int getNextFocusUpId() {
return mNextFocusUpId;
}
@@ -10028,6 +10085,7 @@
*
* @attr ref android.R.styleable#View_nextFocusDown
*/
+ @InspectableProperty(name = "nextFocusDown")
public int getNextFocusDownId() {
return mNextFocusDownId;
}
@@ -10049,6 +10107,7 @@
*
* @attr ref android.R.styleable#View_nextFocusForward
*/
+ @InspectableProperty(name = "nextFocusForward")
public int getNextFocusForwardId() {
return mNextFocusForwardId;
}
@@ -10071,6 +10130,7 @@
*
* @attr ref android.R.styleable#View_nextClusterForward
*/
+ @InspectableProperty(name = "nextClusterForward")
public int getNextClusterForwardId() {
return mNextClusterForwardId;
}
@@ -10437,6 +10497,7 @@
* @see #setSystemUiVisibility(int)
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean getFitsSystemWindows() {
return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
}
@@ -10498,6 +10559,11 @@
@ViewDebug.IntToString(from = INVISIBLE, to = "INVISIBLE"),
@ViewDebug.IntToString(from = GONE, to = "GONE")
})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = VISIBLE, name = "visible"),
+ @EnumMap(value = INVISIBLE, name = "invisible"),
+ @EnumMap(value = GONE, name = "gone")
+ })
@Visibility
public int getVisibility() {
return mViewFlags & VISIBILITY_MASK;
@@ -10521,6 +10587,7 @@
* @return True if this view is enabled, false otherwise.
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isEnabled() {
return (mViewFlags & ENABLED_MASK) == ENABLED;
}
@@ -10690,6 +10757,7 @@
* @attr ref android.R.styleable#View_soundEffectsEnabled
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isSoundEffectsEnabled() {
return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED);
}
@@ -10719,6 +10787,7 @@
* @attr ref android.R.styleable#View_hapticFeedbackEnabled
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isHapticFeedbackEnabled() {
return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED);
}
@@ -10741,6 +10810,12 @@
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"),
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to = "LOCALE")
})
+ @InspectableProperty(hasAttributeId = false, enumMapping = {
+ @EnumMap(value = LAYOUT_DIRECTION_LTR, name = "ltr"),
+ @EnumMap(value = LAYOUT_DIRECTION_RTL, name = "rtl"),
+ @EnumMap(value = LAYOUT_DIRECTION_INHERIT, name = "inherit"),
+ @EnumMap(value = LAYOUT_DIRECTION_LOCALE, name = "locale")
+ })
@LayoutDir
public int getRawLayoutDirection() {
return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
@@ -10794,6 +10869,10 @@
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"),
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = LAYOUT_DIRECTION_LTR, name = "ltr"),
+ @EnumMap(value = LAYOUT_DIRECTION_RTL, name = "rtl")
+ })
@ResolvedLayoutDir
public int getLayoutDirection() {
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
@@ -10980,6 +11059,7 @@
* @attr ref android.R.styleable#View_clickable
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isClickable() {
return (mViewFlags & CLICKABLE) == CLICKABLE;
}
@@ -11007,6 +11087,7 @@
* @see #setLongClickable(boolean)
* @attr ref android.R.styleable#View_longClickable
*/
+ @InspectableProperty
public boolean isLongClickable() {
return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
}
@@ -11032,6 +11113,7 @@
* @see #setContextClickable(boolean)
* @attr ref android.R.styleable#View_contextClickable
*/
+ @InspectableProperty
public boolean isContextClickable() {
return (mViewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
}
@@ -11111,6 +11193,7 @@
* @return true if the view is currently pressed, false otherwise
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty(hasAttributeId = false)
public boolean isPressed() {
return (mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED;
}
@@ -11126,6 +11209,7 @@
* @see #setAssistBlocked(boolean)
* @attr ref android.R.styleable#View_assistBlocked
*/
+ @InspectableProperty
public boolean isAssistBlocked() {
return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0;
}
@@ -11163,6 +11247,7 @@
* @see #setSaveEnabled(boolean)
* @attr ref android.R.styleable#View_saveEnabled
*/
+ @InspectableProperty
public boolean isSaveEnabled() {
return (mViewFlags & SAVE_DISABLED_MASK) != SAVE_DISABLED;
}
@@ -11198,6 +11283,7 @@
* @attr ref android.R.styleable#View_filterTouchesWhenObscured
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean getFilterTouchesWhenObscured() {
return (mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0;
}
@@ -11270,6 +11356,11 @@
@ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"),
@ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO")
}, category = "focus")
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = NOT_FOCUSABLE, name = "false"),
+ @EnumMap(value = FOCUSABLE, name = "true"),
+ @EnumMap(value = FOCUSABLE_AUTO, name = "auto")
+ })
@Focusable
public int getFocusable() {
return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE;
@@ -11284,6 +11375,7 @@
* @attr ref android.R.styleable#View_focusableInTouchMode
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty
public final boolean isFocusableInTouchMode() {
return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE);
}
@@ -11295,6 +11387,7 @@
*
* @return Whether the view should be treated as a focusable unit by screen reader.
*/
+ @InspectableProperty
public boolean isScreenReaderFocusable() {
return (mPrivateFlags3 & PFLAG3_SCREEN_READER_FOCUSABLE) != 0;
}
@@ -11323,6 +11416,7 @@
*
* @attr ref android.R.styleable#View_accessibilityHeading
*/
+ @InspectableProperty
public boolean isAccessibilityHeading() {
return (mPrivateFlags3 & PFLAG3_ACCESSIBILITY_HEADING) != 0;
}
@@ -11377,6 +11471,7 @@
* @attr ref android.R.styleable#View_keyboardNavigationCluster
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty
public final boolean isKeyboardNavigationCluster() {
return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0;
}
@@ -11486,6 +11581,7 @@
* @attr ref android.R.styleable#View_focusedByDefault
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty
public final boolean isFocusedByDefault() {
return (mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0;
}
@@ -11599,6 +11695,7 @@
* @attr ref android.R.styleable#View_defaultFocusHighlightEnabled
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty
public final boolean getDefaultFocusHighlightEnabled() {
return mDefaultFocusHighlightEnabled;
}
@@ -11817,6 +11914,7 @@
*
* @return True if this View is accessibility focused.
*/
+ @InspectableProperty(hasAttributeId = false)
public boolean isAccessibilityFocused() {
return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0;
}
@@ -12143,6 +12241,13 @@
@ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
to = "noHideDescendants")
})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_AUTO, name = "auto"),
+ @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_YES, name = "yes"),
+ @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_NO, name = "no"),
+ @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
+ name = "noHideDescendants"),
+ })
public int getImportantForAccessibility() {
return (mPrivateFlags2 & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK)
>> PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
@@ -12195,6 +12300,11 @@
*
* @see #setAccessibilityLiveRegion(int)
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = ACCESSIBILITY_LIVE_REGION_NONE, name = "none"),
+ @EnumMap(value = ACCESSIBILITY_LIVE_REGION_POLITE, name = "polite"),
+ @EnumMap(value = ACCESSIBILITY_LIVE_REGION_ASSERTIVE, name = "assertive")
+ })
public int getAccessibilityLiveRegion() {
return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK)
>> PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT;
@@ -15042,6 +15152,7 @@
*
* @return The left edge of the displayed part of your view, in pixels.
*/
+ @InspectableProperty
public final int getScrollX() {
return mScrollX;
}
@@ -15053,6 +15164,7 @@
*
* @return The top edge of the displayed part of your view, in pixels.
*/
+ @InspectableProperty
public final int getScrollY() {
return mScrollY;
}
@@ -15289,6 +15401,7 @@
* @return The degrees of rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getRotation() {
return mRenderNode.getRotation();
}
@@ -15329,6 +15442,7 @@
* @return The degrees of Y rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getRotationY() {
return mRenderNode.getRotationY();
}
@@ -15373,6 +15487,7 @@
* @return The degrees of X rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getRotationX() {
return mRenderNode.getRotationX();
}
@@ -15418,6 +15533,7 @@
* @return The scaling factor.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getScaleX() {
return mRenderNode.getScaleX();
}
@@ -15455,6 +15571,7 @@
* @return The scaling factor.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getScaleY() {
return mRenderNode.getScaleY();
}
@@ -15494,6 +15611,7 @@
* @attr ref android.R.styleable#View_transformPivotX
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty(name = "transformPivotX")
public float getPivotX() {
return mRenderNode.getPivotX();
}
@@ -15536,6 +15654,7 @@
* @attr ref android.R.styleable#View_transformPivotY
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty(name = "transformPivotY")
public float getPivotY() {
return mRenderNode.getPivotY();
}
@@ -15594,6 +15713,7 @@
* @return The opacity of the view.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getAlpha() {
return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
}
@@ -15816,6 +15936,7 @@
* @return true if force dark is allowed (default), false if it is disabled
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public boolean isForceDarkAllowed() {
return mRenderNode.isForceDarkAllowed();
}
@@ -16187,6 +16308,7 @@
* @return The base depth position of the view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getElevation() {
return mRenderNode.getElevation();
}
@@ -16215,6 +16337,7 @@
* @return The horizontal position of this view relative to its left position, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getTranslationX() {
return mRenderNode.getTranslationX();
}
@@ -16249,6 +16372,7 @@
* in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getTranslationY() {
return mRenderNode.getTranslationY();
}
@@ -16280,6 +16404,7 @@
* @return The depth of this view relative to its elevation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getTranslationZ() {
return mRenderNode.getTranslationZ();
}
@@ -16324,6 +16449,7 @@
* @return StateListAnimator or null if it does not exists
* @see #setStateListAnimator(android.animation.StateListAnimator)
*/
+ @InspectableProperty
public StateListAnimator getStateListAnimator() {
return mStateListAnimator;
}
@@ -16437,6 +16563,7 @@
*
* @see #setOutlineProvider(ViewOutlineProvider)
*/
+ @InspectableProperty
public ViewOutlineProvider getOutlineProvider() {
return mOutlineProvider;
}
@@ -16513,6 +16640,7 @@
* @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing
* was set
*/
+ @InspectableProperty
public @ColorInt int getOutlineSpotShadowColor() {
return mRenderNode.getSpotShadowColor();
}
@@ -16541,6 +16669,7 @@
* @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if
* nothing was set
*/
+ @InspectableProperty
public @ColorInt int getOutlineAmbientShadowColor() {
return mRenderNode.getAmbientShadowColor();
}
@@ -17720,6 +17849,36 @@
}
/**
+ * Get the fading edge flags, used for inspection.
+ *
+ * @return One of {@link #FADING_EDGE_NONE}, {@link #FADING_EDGE_VERTICAL},
+ * or {@link #FADING_EDGE_HORIZONTAL}
+ * @hide
+ */
+ @InspectableProperty(name = "requiresFadingEdge", flagMapping = {
+ @FlagMap(target = FADING_EDGE_NONE, mask = FADING_EDGE_MASK, name = "none"),
+ @FlagMap(target = FADING_EDGE_VERTICAL, name = "vertical"),
+ @FlagMap(target = FADING_EDGE_HORIZONTAL, name = "horizontal")
+ })
+ int getFadingEdge() {
+ return mViewFlags & FADING_EDGE_MASK;
+ }
+
+ /**
+ * Get the fading edge length, used for inspection
+ *
+ * @return The fading edge length or 0
+ * @hide
+ */
+ @InspectableProperty
+ int getFadingEdgeLength() {
+ if (mScrollCache != null && (mViewFlags & FADING_EDGE_MASK) != FADING_EDGE_NONE) {
+ return mScrollCache.fadingEdgeLength;
+ }
+ return 0;
+ }
+
+ /**
* Returns the strength, or intensity, of the top faded edge. The strength is
* a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
* returns 0.0 or 1.0 but no value in between.
@@ -17883,6 +18042,7 @@
*
* @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
*/
+ @InspectableProperty
public int getScrollBarDefaultDelayBeforeFade() {
return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() :
mScrollCache.scrollBarDefaultDelayBeforeFade;
@@ -17907,6 +18067,7 @@
*
* @attr ref android.R.styleable#View_scrollbarFadeDuration
*/
+ @InspectableProperty
public int getScrollBarFadeDuration() {
return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() :
mScrollCache.scrollBarFadeDuration;
@@ -17931,6 +18092,7 @@
*
* @attr ref android.R.styleable#View_scrollbarSize
*/
+ @InspectableProperty
public int getScrollBarSize() {
return mScrollCache == null ? ViewConfiguration.get(mContext).getScaledScrollBarSize() :
mScrollCache.scrollBarSize;
@@ -17990,6 +18152,12 @@
@ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_OVERLAY, to = "OUTSIDE_OVERLAY"),
@ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_INSET, to = "OUTSIDE_INSET")
})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = SCROLLBARS_INSIDE_OVERLAY, name = "insideOverlay"),
+ @EnumMap(value = SCROLLBARS_INSIDE_INSET, name = "insideInset"),
+ @EnumMap(value = SCROLLBARS_OUTSIDE_OVERLAY, name = "outsideOverlay"),
+ @EnumMap(value = SCROLLBARS_OUTSIDE_INSET, name = "outsideInset")
+ })
@ScrollBarStyle
public int getScrollBarStyle() {
return mViewFlags & SCROLLBARS_STYLE_MASK;
@@ -19411,6 +19579,7 @@
* @see #getDrawableState()
* @see #setDuplicateParentStateEnabled(boolean)
*/
+ @InspectableProperty(name = "duplicateParentState")
public boolean isDuplicateParentStateEnabled() {
return (mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE;
}
@@ -19452,7 +19621,7 @@
*
* @attr ref android.R.styleable#View_layerType
*/
- public void setLayerType(int layerType, @Nullable Paint paint) {
+ public void setLayerType(@LayerType int layerType, @Nullable Paint paint) {
if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
+ "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
@@ -19536,6 +19705,12 @@
* @see #LAYER_TYPE_SOFTWARE
* @see #LAYER_TYPE_HARDWARE
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = LAYER_TYPE_NONE, name = "none"),
+ @EnumMap(value = LAYER_TYPE_SOFTWARE, name = "software"),
+ @EnumMap(value = LAYER_TYPE_HARDWARE, name = "hardware")
+ })
+ @LayerType
public int getLayerType() {
return mLayerType;
}
@@ -22312,6 +22487,7 @@
*
* @attr ref android.R.styleable#View_background
*/
+ @InspectableProperty
public Drawable getBackground() {
return mBackground;
}
@@ -22347,6 +22523,7 @@
* @attr ref android.R.styleable#View_backgroundTint
* @see #setBackgroundTintList(ColorStateList)
*/
+ @InspectableProperty(name = "backgroundTint")
@Nullable
public ColorStateList getBackgroundTintList() {
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
@@ -22383,6 +22560,7 @@
* @see #setBackgroundTintMode(PorterDuff.Mode)
*/
@Nullable
+ @InspectableProperty
public PorterDuff.Mode getBackgroundTintMode() {
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
}
@@ -22418,6 +22596,7 @@
*
* @see #onDrawForeground(Canvas)
*/
+ @InspectableProperty
public Drawable getForeground() {
return mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
}
@@ -22496,6 +22675,7 @@
*
* @attr ref android.R.styleable#View_foregroundGravity
*/
+ @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
public int getForegroundGravity() {
return mForegroundInfo != null ? mForegroundInfo.mGravity
: Gravity.START | Gravity.TOP;
@@ -22563,6 +22743,7 @@
* @attr ref android.R.styleable#View_foregroundTint
* @see #setForegroundTintList(ColorStateList)
*/
+ @InspectableProperty(name = "foregroundTint")
@Nullable
public ColorStateList getForegroundTintList() {
return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
@@ -22602,6 +22783,7 @@
* @attr ref android.R.styleable#View_foregroundTintMode
* @see #setForegroundTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getForegroundTintMode() {
return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
@@ -22846,6 +23028,7 @@
*
* @return the top padding in pixels
*/
+ @InspectableProperty
public int getPaddingTop() {
return mPaddingTop;
}
@@ -22857,6 +23040,7 @@
*
* @return the bottom padding in pixels
*/
+ @InspectableProperty
public int getPaddingBottom() {
return mPaddingBottom;
}
@@ -22868,6 +23052,7 @@
*
* @return the left padding in pixels
*/
+ @InspectableProperty
public int getPaddingLeft() {
if (!isPaddingResolved()) {
resolvePadding();
@@ -22897,6 +23082,7 @@
*
* @return the right padding in pixels
*/
+ @InspectableProperty
public int getPaddingRight() {
if (!isPaddingResolved()) {
resolvePadding();
@@ -23019,6 +23205,7 @@
* @return true if the view is selected, false otherwise
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty(hasAttributeId = false)
public boolean isSelected() {
return (mPrivateFlags & PFLAG_SELECTED) != 0;
}
@@ -23062,6 +23249,7 @@
* @return true if the view is activated, false otherwise
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty(hasAttributeId = false)
public boolean isActivated() {
return (mPrivateFlags & PFLAG_ACTIVATED) != 0;
}
@@ -23560,6 +23748,7 @@
*/
@IdRes
@ViewDebug.CapturedViewProperty
+ @InspectableProperty
public int getId() {
return mID;
}
@@ -23584,6 +23773,7 @@
* @see #getTag(int)
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public Object getTag() {
return mTag;
}
@@ -23780,6 +23970,7 @@
* if baseline alignment is not supported
*/
@ViewDebug.ExportedProperty(category = "layout")
+ @InspectableProperty
public int getBaseline() {
return -1;
}
@@ -24150,6 +24341,7 @@
*
* @attr ref android.R.styleable#View_minHeight
*/
+ @InspectableProperty(name = "minHeight")
public int getMinimumHeight() {
return mMinHeight;
}
@@ -24180,6 +24372,7 @@
*
* @attr ref android.R.styleable#View_minWidth
*/
+ @InspectableProperty(name = "minWidth")
public int getMinimumWidth() {
return mMinWidth;
}
@@ -25199,6 +25392,11 @@
*
* @return This view's over-scroll mode.
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = OVER_SCROLL_ALWAYS, name = "always"),
+ @EnumMap(value = OVER_SCROLL_IF_CONTENT_SCROLLS, name = "ifContentScrolls"),
+ @EnumMap(value = OVER_SCROLL_NEVER, name = "never")
+ })
public int getOverScrollMode() {
return mOverScrollMode;
}
@@ -25257,6 +25455,7 @@
*
* @see #setNestedScrollingEnabled(boolean)
*/
+ @InspectableProperty
public boolean isNestedScrollingEnabled() {
return (mPrivateFlags3 & PFLAG3_NESTED_SCROLLING_ENABLED) ==
PFLAG3_NESTED_SCROLLING_ENABLED;
@@ -25586,6 +25785,16 @@
@ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
})
+ @InspectableProperty(hasAttributeId = false, enumMapping = {
+ @EnumMap(value = TEXT_DIRECTION_INHERIT, name = "inherit"),
+ @EnumMap(value = TEXT_DIRECTION_LOCALE, name = "locale"),
+ @EnumMap(value = TEXT_DIRECTION_ANY_RTL, name = "anyRtl"),
+ @EnumMap(value = TEXT_DIRECTION_LTR, name = "ltr"),
+ @EnumMap(value = TEXT_DIRECTION_RTL, name = "rtl"),
+ @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG, name = "firstStrong"),
+ @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name = "firstStrongLtr"),
+ @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name = "firstStrongRtl"),
+ })
@UnsupportedAppUsage
public int getRawTextDirection() {
return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_MASK) >> PFLAG2_TEXT_DIRECTION_MASK_SHIFT;
@@ -25653,6 +25862,15 @@
@ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
})
+ @InspectableProperty(hasAttributeId = false, enumMapping = {
+ @EnumMap(value = TEXT_DIRECTION_LOCALE, name = "locale"),
+ @EnumMap(value = TEXT_DIRECTION_ANY_RTL, name = "anyRtl"),
+ @EnumMap(value = TEXT_DIRECTION_LTR, name = "ltr"),
+ @EnumMap(value = TEXT_DIRECTION_RTL, name = "rtl"),
+ @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG, name = "firstStrong"),
+ @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name = "firstStrongLtr"),
+ @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name = "firstStrongRtl"),
+ })
public int getTextDirection() {
return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
}
@@ -25824,6 +26042,15 @@
@ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
@ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
})
+ @InspectableProperty(hasAttributeId = false, enumMapping = {
+ @EnumMap(value = TEXT_ALIGNMENT_INHERIT, name = "inherit"),
+ @EnumMap(value = TEXT_ALIGNMENT_GRAVITY, name = "gravity"),
+ @EnumMap(value = TEXT_ALIGNMENT_TEXT_START, name = "textStart"),
+ @EnumMap(value = TEXT_ALIGNMENT_TEXT_END, name = "textEnd"),
+ @EnumMap(value = TEXT_ALIGNMENT_CENTER, name = "center"),
+ @EnumMap(value = TEXT_ALIGNMENT_VIEW_START, name = "viewStart"),
+ @EnumMap(value = TEXT_ALIGNMENT_VIEW_END, name = "viewEnd")
+ })
@TextAlignment
@UnsupportedAppUsage
public int getRawTextAlignment() {
@@ -25890,6 +26117,14 @@
@ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
@ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = TEXT_ALIGNMENT_GRAVITY, name = "gravity"),
+ @EnumMap(value = TEXT_ALIGNMENT_TEXT_START, name = "textStart"),
+ @EnumMap(value = TEXT_ALIGNMENT_TEXT_END, name = "textEnd"),
+ @EnumMap(value = TEXT_ALIGNMENT_CENTER, name = "center"),
+ @EnumMap(value = TEXT_ALIGNMENT_VIEW_START, name = "viewStart"),
+ @EnumMap(value = TEXT_ALIGNMENT_VIEW_END, name = "viewEnd")
+ })
@TextAlignment
public int getTextAlignment() {
return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK) >>
@@ -26122,6 +26357,7 @@
/**
* Gets the pointer icon for the current view.
*/
+ @InspectableProperty
public PointerIcon getPointerIcon() {
return mPointerIcon;
}
@@ -26672,6 +26908,7 @@
* if no name has been given.
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public String getTransitionName() {
return mTransitionName;
}
@@ -28277,6 +28514,7 @@
* @see #setTooltipText(CharSequence)
* @attr ref android.R.styleable#View_tooltipText
*/
+ @InspectableProperty
@Nullable
public CharSequence getTooltipText() {
return mTooltipInfo != null ? mTooltipInfo.mTooltipText : null;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cb47886..9fe0ddc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -124,6 +124,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.List;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
@@ -200,8 +201,6 @@
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
static boolean sFirstDrawComplete = false;
- private FrameDrawingCallback mNextRtFrameCallback;
-
/**
* Callback for notifying about global configuration changes.
*/
@@ -464,7 +463,7 @@
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeNavBar;
- private InsetsState mPendingInsets = new InsetsState();
+ private InsetsState mTempInsets = new InsetsState();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -545,6 +544,8 @@
private boolean mNeedsRendererSetup;
+ private final InputEventCompatProcessor mInputCompatProcessor;
+
/**
* Consistency verifier for debugging purposes.
*/
@@ -552,7 +553,7 @@
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
- private final InsetsController mInsetsController = new InsetsController();
+ private final InsetsController mInsetsController = new InsetsController(this);
static final class SystemUiVisibilityInfo {
int seq;
@@ -600,6 +601,25 @@
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ String processorOverrideName = context.getResources().getString(
+ R.string.config_inputEventCompatProcessorOverrideClassName);
+ if (processorOverrideName.isEmpty()) {
+ // No compatibility processor override, using default.
+ mInputCompatProcessor = new InputEventCompatProcessor(context);
+ } else {
+ InputEventCompatProcessor compatProcessor = null;
+ try {
+ final Class<? extends InputEventCompatProcessor> klass =
+ (Class<? extends InputEventCompatProcessor>) Class.forName(
+ processorOverrideName);
+ compatProcessor = klass.getConstructor(Context.class).newInstance(context);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to create the InputEventCompatProcessor. ", e);
+ } finally {
+ mInputCompatProcessor = compatProcessor;
+ }
+ }
+
if (!sCompatibilityDone) {
sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
@@ -823,7 +843,7 @@
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
- mInsetsController.getState());
+ mTempInsets);
setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
@@ -851,7 +871,7 @@
mAttachInfo.mAlwaysConsumeNavBar =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
- mPendingInsets = mInsetsController.getState();
+ mInsetsController.onStateChanged(mTempInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1052,7 +1072,9 @@
* @param callback The callback to register.
*/
public void registerRtFrameCallback(FrameDrawingCallback callback) {
- mNextRtFrameCallback = callback;
+ if (mAttachInfo.mThreadedRenderer != null) {
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(callback);
+ }
}
@UnsupportedAppUsage
@@ -1342,6 +1364,19 @@
scheduleTraversals();
}
+ void notifyInsetsChanged() {
+ if (!USE_NEW_INSETS) {
+ return;
+ }
+ mApplyInsetsRequested = true;
+
+ // If this changes during traversal, no need to schedule another one as it will dispatch it
+ // during the current traversal.
+ if (!mIsInTraversal) {
+ scheduleTraversals();
+ }
+ }
+
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
@@ -2027,9 +2062,6 @@
if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
insetsChanged = true;
}
- if (!mPendingInsets.equals(mInsetsController.getState())) {
- insetsChanged = true;
- }
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
@@ -2223,8 +2255,6 @@
mAttachInfo.mStableInsets);
final boolean cutoutChanged = !mPendingDisplayCutout.equals(
mAttachInfo.mDisplayCutout);
- final boolean insetsStateChanged = !mPendingInsets.equals(
- mInsetsController.getState());
final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
final boolean surfaceSizeChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -2262,10 +2292,6 @@
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
- if (insetsStateChanged) {
- mInsetsController.setState(mPendingInsets);
- contentInsetsChanged = true;
- }
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -3534,10 +3560,7 @@
useAsyncReport = true;
- // draw(...) might invoke post-draw, which might register the next callback already.
- final FrameDrawingCallback callback = mNextRtFrameCallback;
- mNextRtFrameCallback = null;
- mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback);
+ mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
@@ -4378,22 +4401,12 @@
}
break;
case MSG_INSETS_CHANGED:
- mPendingInsets = (InsetsState) msg.obj;
-
- // TODO: Full traversal not needed here.
- if (USE_NEW_INSETS) {
- requestLayout();
- }
+ mInsetsController.onStateChanged((InsetsState) msg.obj);
break;
case MSG_INSETS_CONTROL_CHANGED: {
SomeArgs args = (SomeArgs) msg.obj;
- mPendingInsets = (InsetsState) args.arg1;
mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
-
- // TODO: Full traversal not necessarily needed here.
- if (USE_NEW_INSETS) {
- requestLayout();
- }
+ mInsetsController.onStateChanged((InsetsState) args.arg1);
break;
}
case MSG_WINDOW_MOVED:
@@ -6794,7 +6807,7 @@
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurface, mPendingInsets);
+ mPendingMergedConfiguration, mSurface, mTempInsets);
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6811,7 +6824,7 @@
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
setFrame(mTmpFrame);
-
+ mInsetsController.onStateChanged(mTempInsets);
return relayoutResult;
}
@@ -7175,6 +7188,7 @@
public static final int FLAG_FINISHED_HANDLED = 1 << 3;
public static final int FLAG_RESYNTHESIZED = 1 << 4;
public static final int FLAG_UNHANDLED = 1 << 5;
+ public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6;
public QueuedInputEvent mNext;
@@ -7267,7 +7281,6 @@
@UnsupportedAppUsage
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
- adjustInputEventForCompatibility(event);
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
@@ -7370,7 +7383,22 @@
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
- q.mReceiver.finishInputEvent(q.mEvent, handled);
+ boolean modified = (q.mFlags & QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY) != 0;
+ if (modified) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventBeforeFinish");
+ InputEvent processedEvent;
+ try {
+ processedEvent =
+ mInputCompatProcessor.processInputEventBeforeFinish(q.mEvent);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ if (processedEvent != null) {
+ q.mReceiver.finishInputEvent(processedEvent, handled);
+ }
+ } else {
+ q.mReceiver.finishInputEvent(q.mEvent, handled);
+ }
} else {
q.mEvent.recycleIfNeededAfterDispatch();
}
@@ -7378,19 +7406,6 @@
recycleQueuedInputEvent(q);
}
- private void adjustInputEventForCompatibility(InputEvent e) {
- if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
- MotionEvent motion = (MotionEvent) e;
- final int mask =
- MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
- final int buttonState = motion.getButtonState();
- final int compatButtonState = (buttonState & mask) >> 4;
- if (compatButtonState != 0) {
- motion.setButtonState(buttonState | compatButtonState);
- }
- }
- }
-
static boolean isTerminalInputEvent(InputEvent event) {
if (event instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent)event;
@@ -7461,7 +7476,28 @@
@Override
public void onInputEvent(InputEvent event) {
- enqueueInputEvent(event, this, 0, true);
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+ List<InputEvent> processedEvents;
+ try {
+ processedEvents =
+ mInputCompatProcessor.processInputEventForCompatibility(event);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ if (processedEvents != null) {
+ if (processedEvents.isEmpty()) {
+ // InputEvent consumed by mInputCompatProcessor
+ finishInputEvent(event, true);
+ } else {
+ for (int i = 0; i < processedEvents.size(); i++) {
+ enqueueInputEvent(
+ processedEvents.get(i), this,
+ QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
+ }
+ }
+ } else {
+ enqueueInputEvent(event, this, 0, true);
+ }
}
@Override
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index f7c9a0b..763ce4f 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
@@ -749,7 +748,6 @@
*
* @param callback The callback to invoke when the frame is committed.
*/
- @TestApi
public void registerFrameCommitCallback(@NonNull Runnable callback) {
checkIsAlive();
if (mOnFrameCommitListeners == null) {
@@ -772,7 +770,6 @@
* not be invoked. If false is returned then the callback was either never added
* or may already be pending execution and was unable to be removed
*/
- @TestApi
public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
checkIsAlive();
if (mOnFrameCommitListeners == null) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 88b9c80..c5c1bca 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1004,6 +1004,36 @@
}
/**
+ * Returns accessibility window id from window token. Accessibility window id is the one
+ * returned from AccessibilityWindowInfo.getId(). Only available for the system process.
+ *
+ * @param windowToken Window token to find accessibility window id.
+ * @return Accessibility window id for the window token.
+ * AccessibilityWindowInfo.UNDEFINED_WINDOW_ID if accessibility window id not available for
+ * the token.
+ * @hide
+ */
+ @SystemApi
+ public int getAccessibilityWindowId(IBinder windowToken) {
+ if (windowToken == null) {
+ return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ }
+
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ }
+ }
+ try {
+ return service.getAccessibilityWindowId(windowToken);
+ } catch (RemoteException e) {
+ return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ }
+ }
+
+ /**
* Sets the current state and notifies listeners, if necessary.
*
* @param stateFlags The state flags.
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 2767a82..38dac94 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -76,5 +76,8 @@
// System process only
boolean sendFingerprintGesture(int gestureKeyCode);
+ // System process only
+ int getAccessibilityWindowId(IBinder windowToken);
+
long getRecommendedTimeoutMillis();
}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/android/view/contentcapture/ContentCaptureEvent.aidl
similarity index 94%
rename from core/java/android/view/intelligence/ContentCaptureEvent.aidl
rename to core/java/android/view/contentcapture/ContentCaptureEvent.aidl
index c66a6cb..abaf9ed 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package android.view.contentcapture;
parcelable ContentCaptureEvent;
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
similarity index 91%
rename from core/java/android/view/intelligence/ContentCaptureEvent.java
rename to core/java/android/view/contentcapture/ContentCaptureEvent.java
index f636281..66fa530 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.view.intelligence;
+package android.view.contentcapture;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -42,22 +42,38 @@
/**
* Called when the activity is started.
+ *
+ * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
+ * something related to a session and/or domain.
*/
+ @Deprecated
public static final int TYPE_ACTIVITY_STARTED = 1;
/**
* Called when the activity is resumed.
+ *
+ * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
+ * something related to a session and/or domain.
*/
+ @Deprecated
public static final int TYPE_ACTIVITY_RESUMED = 2;
/**
* Called when the activity is paused.
+ *
+ * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
+ * something related to a session and/or domain.
*/
+ @Deprecated
public static final int TYPE_ACTIVITY_PAUSED = 3;
/**
* Called when the activity is stopped.
+ *
+ * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
+ * something related to a session and/or domain.
*/
+ @Deprecated
public static final int TYPE_ACTIVITY_STOPPED = 4;
/**
@@ -163,7 +179,7 @@
* Gets optional flags associated with the event.
*
* @return either {@code 0} or
- * {@link android.view.intelligence.ContentCaptureManager#FLAG_USER_INPUT}.
+ * {@link android.view.contentcapture.ContentCaptureManager#FLAG_USER_INPUT}.
*/
public int getFlags() {
return mFlags;
diff --git a/core/java/android/view/intelligence/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
similarity index 95%
rename from core/java/android/view/intelligence/ContentCaptureManager.java
rename to core/java/android/view/contentcapture/ContentCaptureManager.java
index 45518d5..1889692 100644
--- a/core/java/android/view/intelligence/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.view.intelligence;
+package android.view.contentcapture;
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_APPEARED;
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -31,18 +31,18 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
-import android.service.intelligence.InteractionSessionId;
import android.util.Log;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
-import android.view.intelligence.ContentCaptureEvent.EventType;
+import android.view.contentcapture.ContentCaptureEvent.EventType;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
/*
@@ -58,7 +58,7 @@
@SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE)
public final class ContentCaptureManager {
- private static final String TAG = "ContentCaptureManager";
+ private static final String TAG = ContentCaptureManager.class.getSimpleName();
// TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
private static final boolean VERBOSE = false;
@@ -113,10 +113,10 @@
private final Context mContext;
@Nullable
- private final IIntelligenceManager mService;
+ private final IContentCaptureManager mService;
@Nullable
- private InteractionSessionId mId;
+ private String mId;
private int mState = STATE_UNKNOWN;
@@ -137,7 +137,8 @@
private final Handler mHandler;
/** @hide */
- public ContentCaptureManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
+ public ContentCaptureManager(@NonNull Context context,
+ @Nullable IContentCaptureManager service) {
mContext = Preconditions.checkNotNull(context, "context cannot be null");
if (VERBOSE) {
Log.v(TAG, "Constructor for " + context.getPackageName());
@@ -165,7 +166,7 @@
return;
}
mState = STATE_WAITING_FOR_SERVER;
- mId = new InteractionSessionId();
+ mId = UUID.randomUUID().toString();
mApplicationToken = token;
mComponentName = componentName;
@@ -421,7 +422,7 @@
/** @hide */
public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.println("IntelligenceManager");
+ pw.print(prefix); pw.println("ContentCaptureManager");
final String prefix2 = prefix + " ";
pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
new file mode 100644
index 0000000..8704dad
--- /dev/null
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.content.ComponentName;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.os.IBinder;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+oneway interface IContentCaptureManager {
+ void startSession(int userId, IBinder activityToken, in ComponentName componentName,
+ String sessionId, int flags, in IResultReceiver result);
+ void finishSession(int userId, String sessionId, in List<ContentCaptureEvent> events);
+ void sendEvents(int userId, in String sessionId, in List<ContentCaptureEvent> events);
+}
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
similarity index 99%
rename from core/java/android/view/intelligence/ViewNode.java
rename to core/java/android/view/contentcapture/ViewNode.java
index ea57461..86b89adb 100644
--- a/core/java/android/view/intelligence/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.view.intelligence;
+package android.view.contentcapture;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1340955..e57fdff 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2354,17 +2354,19 @@
}
/**
- * Shows the input method chooser dialog.
+ * Shows the input method chooser dialog from system.
*
* @param showAuxiliarySubtypes Set true to show auxiliary input methods.
+ * @param displayId The ID of the display where the chooser dialog should be shown.
* @hide
*/
- public void showInputMethodPicker(boolean showAuxiliarySubtypes) {
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public void showInputMethodPickerFromSystem(boolean showAuxiliarySubtypes, int displayId) {
final int mode = showAuxiliarySubtypes
? SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES
: SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
try {
- mService.showInputMethodPickerFromClient(mClient, mode);
+ mService.showInputMethodPickerFromSystem(mClient, mode, displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2520,16 +2522,6 @@
*/
@Deprecated
public boolean switchToLastInputMethod(IBinder imeToken) {
- if (imeToken == null) {
- // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
- // TODO(Bug 114488811): Consider deprecating null token rule.
- try {
- return mService.switchToPreviousInputMethod(imeToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
}
@@ -2548,16 +2540,6 @@
*/
@Deprecated
public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
- if (imeToken == null) {
- // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
- // TODO(Bug 114488811): Consider deprecating null token rule.
- try {
- return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
return InputMethodPrivilegedOperationsRegistry.get(imeToken)
.switchToNextInputMethod(onlyCurrentIme);
}
diff --git a/core/java/android/view/inspector/ChildTraverser.java b/core/java/android/view/inspector/ChildTraverser.java
deleted file mode 100644
index b775de5..0000000
--- a/core/java/android/view/inspector/ChildTraverser.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inspector;
-
-import android.annotation.NonNull;
-
-/**
- * Interface for visiting all the child nodes of an inspectable object.
- *
- * Inspectable objects may return a collection of children as an array, an {@link Iterable} or an
- * {@link java.util.Iterator}. This provides a unified API for traversing across all the children
- * of an inspectable node.
- *
- * This interface is consumed by {@link InspectionHelper#traverseChildren(Object, ChildTraverser)}
- * and may be implemented as a lambda.
- *
- * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
- * @hide
- */
-@FunctionalInterface
-public interface ChildTraverser {
- /**
- * Visit one child object of a parent inspectable object.
- *
- * The iteration interface will filter null values out before passing them to this method, but
- * some child objects may not be inspectable. It is up to the implementor to determine their
- * inspectablity and what to do with them.
- *
- * @param child A child object, guaranteed not to be null.
- */
- void traverseChild(@NonNull Object child);
-}
diff --git a/core/java/android/view/inspector/InspectableChildren.java b/core/java/android/view/inspector/InspectableChildren.java
deleted file mode 100644
index de8fa29..0000000
--- a/core/java/android/view/inspector/InspectableChildren.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inspector;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Marks a getter for an inspectable node's inspectable children.
- *
- * This annotation can be applied to any getter that returns a collection of objects, either an
- * array, an {@link Iterable} or a {@link java.util.Iterator}. The getter may return null, which
- * will be treated as an empty collection. Additionally, the inspector will discard any null
- * entries in the collection.
- *
- * By default, this annotation is inherited. At runtime, the inspector introspects on the class
- * hierachy and uses the annotated getter from the bottommost class, if different from any
- * annoated getters of the parent class. If a class inherits from a parent class with an annotated
- * getter, but does not include this annotation, the child class will be traversed using the
- * getter annotated on the parent. This holds true even if the child class overrides the getter.
- *
- * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
- * @see InspectionHelper#hasChildTraversal()
- * @hide
- */
-@Target({METHOD})
-@Retention(SOURCE)
-public @interface InspectableChildren {
-}
diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java
index 716409c..ea94ad4 100644
--- a/core/java/android/view/inspector/InspectableNodeName.java
+++ b/core/java/android/view/inspector/InspectableNodeName.java
@@ -34,7 +34,7 @@
* This annotation does not inherit. If a class extends an annotated parent class, but does not
* annotate itself, its node name will be inferred from its Java name.
*
- * @see InspectionHelper#getNodeName()
+ * @see InspectionCompanion#getNodeName()
* @hide
*/
@Target({TYPE})
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index b0fd503..5b957156 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -17,8 +17,11 @@
package android.view.inspector;
import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.content.res.ResourceId;
+
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -31,8 +34,8 @@
* but on a different getter, the inspector will use the child's getter when inspecting instances
* of the child, and the parent's otherwise.
*
- * @see InspectionHelper#mapProperties(PropertyMapper)
- * @see InspectionHelper#readProperties(Object, PropertyReader)
+ * @see InspectionCompanion#mapProperties(PropertyMapper)
+ * @see InspectionCompanion#readProperties(Object, PropertyReader)
* @hide
*/
@Target({METHOD})
@@ -46,5 +49,171 @@
*
* @return The name of the property.
*/
- String value() default "";
+ String name() default "";
+
+ /**
+ * If the property is inflated from XML, the resource ID of its XML attribute.
+ *
+ * If left as {ID_NULL}, and {@link #hasAttributeId()} is true, the attribute ID will be
+ * inferred from {@link #name()}.
+ *
+ * @return The attribute ID of the property or {@link ResourceId#ID_NULL}
+ */
+ int attributeId() default ResourceId.ID_NULL;
+
+ /**
+ * If this property has an attribute ID.
+ *
+ * Set to false if the annotated property does not have an attribute ID, that is, it is not
+ * inflated from an XML attribute. This will prevent the automatic inference of the attribute
+ * ID if {@link #attributeId()} is set to {@link ResourceId#ID_NULL}.
+ *
+ * @return Whether to infer an attribute ID if not supplied
+ */
+ boolean hasAttributeId() default true;
+
+ /**
+ * Specify how to interpret a value type packed into a primitive integer.
+ *
+ * @return A {@link ValueType}
+ */
+ ValueType valueType() default ValueType.INFERRED;
+
+ /**
+ * For enumerations packed into primitive {int} properties, map the values to string names.
+ *
+ * Note that {@link #enumMapping()} cannot be used simultaneously with {@link #flagMapping()}.
+ *
+ * @return An array of {@link EnumMap}, empty if not applicable
+ * @see android.annotation.IntDef
+ * @see IntEnumMapping
+ */
+ EnumMap[] enumMapping() default {};
+
+ /**
+ * For flags packed into primitive {int} properties, model the string names of the flags.
+ *
+ * Note that {@link #flagMapping()} cannot be used simultaneously with {@link #enumMapping()}.
+ *
+ * @return An array of {@link FlagMap}, empty if not applicable
+ * @see android.annotation.IntDef
+ * @see IntFlagMapping
+ */
+ FlagMap[] flagMapping() default {};
+
+
+ /**
+ * One entry in an enumeration packed into a primitive {int}.
+ *
+ * @see IntEnumMapping
+ * @hide
+ */
+ @Target({TYPE})
+ @Retention(SOURCE)
+ @interface EnumMap {
+ /**
+ * The string name of this enumeration value.
+ *
+ * @return A string name
+ */
+ String name();
+
+ /**
+ * The integer value of this enumeration value.
+ *
+ * @return An integer value
+ */
+ int value();
+ }
+
+ /**
+ * One flag value of many that may be packed into a primitive {int}.
+ *
+ * @see IntFlagMapping
+ * @hide
+ */
+ @Target({TYPE})
+ @Retention(SOURCE)
+ @interface FlagMap {
+ /**
+ * The string name of this flag.
+ *
+ * @return A string name
+ */
+ String name();
+
+ /**
+ * A target value that the property's value must equal after masking.
+ *
+ * If a mask is not supplied (i.e., {@link #mask()} is 0), the target will be reused as the
+ * mask. This handles the common case where no flags mutually exclude each other.
+ *
+ * @return The target value to compare against
+ */
+ int target();
+
+ /**
+ * A mask that the property will be bitwise anded with before comparing to the target.
+ *
+ * If set to 0 (the default), the value of {@link #target()} will be used as a mask. Zero
+ * was chosen as the default since bitwise and with zero is always zero.
+ *
+ * @return A mask, or 0 to use the target as a mask
+ */
+ int mask() default 0;
+ }
+
+ /**
+ * The type of value packed into a primitive {int}.
+ *
+ * @hide
+ */
+ enum ValueType {
+ /**
+ * No special handling, property is considered to be a numeric value.
+ */
+ NONE,
+
+ /**
+ * The default the annotation processor infers the value type from context.
+ */
+ INFERRED,
+
+ /**
+ * Value packs an enumeration.
+ *
+ * This is inferred if {@link #enumMapping()} is specified.
+ *
+ * @see EnumMap
+ */
+ INT_ENUM,
+
+ /**
+ * Value packs flags, of which many may be enabled at once.
+ *
+ * This is inferred if {@link #flagMapping()} is specified.
+ *
+ * @see FlagMap
+ */
+ INT_FLAG,
+
+ /**
+ * Value packs color information.
+ *
+ * This is inferred from {@link android.annotation.ColorInt}, or
+ * {@link android.annotation.ColorLong} on the getter method.
+ *
+ * @see android.graphics.Color
+ */
+ COLOR,
+
+ /**
+ * Value packs gravity information.
+ *
+ * This type is not inferred, and is non-trivial to represent using {@link FlagMap}.
+ *
+ * @see android.view.Gravity
+ */
+ GRAVITY
+ }
}
diff --git a/core/java/android/view/inspector/InspectionCompanion.java b/core/java/android/view/inspector/InspectionCompanion.java
new file mode 100644
index 0000000..62d769b
--- /dev/null
+++ b/core/java/android/view/inspector/InspectionCompanion.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * An interface for companion objects used to inspect views.
+ *
+ * Inspection companions only need to handle the properties and node name of the specific class
+ * they are defined for, not anything from a parent class. At runtime, the inspector instantiates
+ * one instance of each inspection companion, and handles visiting them in the correct inheritance
+ * order for each type it inspects.
+ *
+ * Properties are read from the top of the type tree to the bottom, so that classes that override
+ * a property in their parent class can overwrite it in the reader. In general, properties will
+ * cleanly inherit through their getters, and the inspector runtime will read the properties of a
+ * parent class via the parent's inspection companion, and the child companion will only read
+ * properties added or changed since the parent was defined.
+ *
+ * Only one child traversal is considered for each class. If a descendant class defines a
+ * different child traversal than its parent, only the bottom traversal is used. If a class does
+ * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's
+ * traversal will be used.
+ *
+ * @param <T> The type of inspectable this is the companion to
+ */
+public interface InspectionCompanion<T> {
+ /**
+ * Map the string names of the properties this companion knows about to integer IDs.
+ *
+ * Each companion is responsible for storing the integer IDs of all its properties. This is the
+ * only method that is allowed to modify the stored IDs.
+ *
+ * Calling {@link #readProperties(T, PropertyReader)} before calling this results in
+ * undefined behavior.
+ *
+ * @param propertyMapper A {@link PropertyMapper} maps string names to IDs.
+ */
+ void mapProperties(@NonNull PropertyMapper propertyMapper);
+
+ /**
+ * Read the values of an instance of this companion's type into a {@link PropertyReader}.
+ *
+ * This method needs to return the property IDs stored by
+ * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties
+ * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
+ * called before {mapProperties}.
+ *
+ * @param inspectable A object of type {T} to read the properties of.
+ * @param propertyReader An object which receives the property IDs and values.
+ */
+ void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
+
+ /**
+ * Get an optional name to display to developers for inspection nodes of this companion's type.
+ *
+ * The default implementation returns null, which will cause the runtime to use the class's
+ * simple name as defined by {@link Class#getSimpleName()} as the node name.
+ *
+ * If the type of this companion is inflated from XML, this method should be overridden to
+ * return the string used as the tag name for this type in XML.
+ *
+ * @return A string to use as the node name, or null to use the simple class name fallback.
+ */
+ @Nullable
+ default String getNodeName() {
+ return null;
+ }
+
+ /**
+ * Thrown by {@link #readProperties(Object, PropertyReader)} if called before
+ * {@link #mapProperties(PropertyMapper)}.
+ */
+ class UninitializedPropertyMapException extends RuntimeException {
+ public UninitializedPropertyMapException() {
+ super("Unable to read properties of an inspectable before mapping their IDs.");
+ }
+ }
+}
diff --git a/core/java/android/view/inspector/InspectionHelper.java b/core/java/android/view/inspector/InspectionHelper.java
deleted file mode 100644
index 27a9704..0000000
--- a/core/java/android/view/inspector/InspectionHelper.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inspector;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * An interface for companion objects used to inspect views.
- *
- * Inspection helpers only need to handle the properties, name and traversal of the specific class
- * they are defined for, not anything from a parent class. At runtime, the inspector instantiates
- * one instance of each inspection helper, and handles visiting them in the correct inheritance
- * order for each type it inspects.
- *
- * Properties are read from the top of the type tree to the bottom, so that classes that override
- * a property in their parent class can overwrite it in the reader. In general, properties will
- * cleanly inherit through their getters, and the inspector runtime will read the properties of a
- * parent class via the parent's inspection helper, and the child helper will only read properties
- * added or changed since the parent was defined.
- *
- * Only one child traversal is considered for each class. If a descendant class defines a
- * different child traversal than its parent, only the bottom traversal is used. If a class does
- * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's
- * traversal will be used.
- *
- * @param <T> The type of inspectable this helper operates on
- * @hide
- */
-public interface InspectionHelper<T> {
- /**
- * Map the string names of the properties this helper knows about to integer IDs.
- *
- * Each helper is responsible for storing the integer IDs of all its properties. This is the
- * only method that is allowed to modify the stored IDs.
- *
- * Calling {@link #readProperties(T, PropertyReader)} before calling this results in
- * undefined behavior.
- *
- * @param propertyMapper A {@link PropertyMapper} or lambda which maps string names to IDs.
- */
- void mapProperties(@NonNull PropertyMapper propertyMapper);
-
- /**
- * Read the values of an instance of this helper's type into a {@link PropertyReader}.
- *
- * This method needs to return the property IDs stored by
- * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties
- * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
- * called before {mapProperties}.
- *
- * @param inspectable A object of type {@link T} to read the properties of.
- * @param propertyReader An object which receives the property IDs and values.
- */
- void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
-
- /**
- * Query if this inspectable type can potentially have child nodes.
- *
- * E.g.: any descendant of {@link android.view.ViewGroup} can have child nodes, but a leaf
- * view like {@link android.widget.ImageView} may not.
- *
- * The default implementation always returns false. If an implementing class overrides this, it
- * should also define {@link #traverseChildren(T, ChildTraverser)}.
- *
- * @return True if this inspectable type can potentially have child nodes, false otherwise.
- */
- default boolean hasChildTraversal() {
- return false;
- }
-
- /**
- * Traverse the child nodes of an instance of this helper's type into a {@link ChildTraverser}.
- *
- * This provides the ability to traverse over a variety of collection APIs (e.g.: arrays,
- * {@link Iterable}, or {@link java.util.Iterator}) in a uniform fashion. The traversal must be
- * in the order defined by this helper's type. If the getter returns null, the helper must
- * treat it as an empty collection.
- *
- * The default implementation throws a {@link NoChildTraversalException}. If
- * {@link #hasChildTraversal()} returns is overriden to return true, it is expected that the
- * implementing class will also override this method and provide a traversal.
- *
- * @param inspectable An object of type {@link T} to traverse the child nodes of.
- * @param childTraverser A {@link ChildTraverser} or lamba to receive the children in order.
- * @throws NoChildTraversalException If there is no defined child traversal
- */
- default void traverseChildren(
- @NonNull T inspectable,
- @SuppressWarnings("unused") @NonNull ChildTraverser childTraverser) {
- throw new NoChildTraversalException(inspectable.getClass());
- }
-
- /**
- * Get an optional name to display to developers for inspection nodes of this helper's type.
- *
- * The default implementation returns null, which will cause the runtime to use the class's
- * simple name as defined by {@link Class#getSimpleName()} as the node name.
- *
- * If the type of this helper is inflated from XML, this method should be overridden to return
- * the string used as the tag name for this type in XML.
- *
- * @return A string to use as the node name, or null to use the simple class name fallback.
- */
- @Nullable
- default String getNodeName() {
- return null;
- }
-
- /**
- * Thrown by {@link #readProperties(Object, PropertyReader)} if called before
- * {@link #mapProperties(PropertyMapper)}.
- */
- class UninitializedPropertyMapException extends RuntimeException {
- public UninitializedPropertyMapException() {
- super("Unable to read properties of an inspectable before mapping their IDs.");
- }
- }
-
- /**
- * Thrown by {@link #traverseChildren(Object, ChildTraverser)} if no child traversal exists.
- */
- class NoChildTraversalException extends RuntimeException {
- public NoChildTraversalException(Class cls) {
- super(String.format(
- "Class %s does not have a defined child traversal. Cannot traverse children.",
- cls.getCanonicalName()
- ));
- }
- }
-}
diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java
new file mode 100644
index 0000000..69f6dce
--- /dev/null
+++ b/core/java/android/view/inspector/IntEnumMapping.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Maps the values of an {int} property to string names for properties that encode enumerations.
+ *
+ * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
+ * for enumerations packed into primitive {int} properties.
+ *
+ * This class is immutable, and must be constructed by a {@link Builder}.
+ *
+ * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping)
+ */
+public final class IntEnumMapping {
+ private final Value[] mValues;
+
+ /**
+ * Map from a property value to a string name.
+ *
+ * @param value The value of a property
+ * @return The name of the enumeration value, null if the value is not mapped
+ */
+ @Nullable
+ public String nameOf(int value) {
+ for (Value valueTuple : mValues) {
+ if (valueTuple.mValue == value) {
+ return valueTuple.mName;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a new instance from a builder.
+ *
+ * This constructor is private, use {@link Builder#build()} instead.
+ *
+ * @param builder A builder to create from
+ */
+ private IntEnumMapping(Builder builder) {
+ mValues = builder.mValues.toArray(new Value[builder.mValues.size()]);
+ }
+
+ /**
+ * A builder for {@link IntEnumMapping}
+ */
+ public static final class Builder {
+ private final ArrayList<Value> mValues;
+
+ public Builder() {
+ mValues = new ArrayList<>();
+ }
+
+ /**
+ * Add a new entry to this mapping.
+ *
+ * @param name Name of the enumeration value
+ * @param value Int value of the enumeration value
+ * @return This builder
+ */
+ @NonNull
+ public Builder addValue(@NonNull String name, int value) {
+ mValues.add(new Value(name, value));
+ return this;
+ }
+
+ /**
+ * Clear the builder, allowing for recycling.
+ */
+ public void clear() {
+ mValues.clear();
+ }
+
+ /**
+ * Build a new {@link IntEnumMapping} from this builder
+ *
+ * @return A new mapping
+ */
+ @NonNull
+ public IntEnumMapping build() {
+ return new IntEnumMapping(this);
+ }
+ }
+
+ /**
+ * Inner class that holds the name and value of an enumeration value.
+ */
+ private static final class Value {
+ @NonNull private final String mName;
+ private final int mValue;
+
+ private Value(@NonNull String name, int value) {
+ mName = name;
+ mValue = value;
+ }
+ }
+}
diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java
new file mode 100644
index 0000000..dcb87e1
--- /dev/null
+++ b/core/java/android/view/inspector/IntFlagMapping.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inspector;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+
+/**
+ * Maps the values of an {int} property to arrays of string for properties that encode flags.
+ *
+ * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
+ * for flag values packed into primitive {int} properties.
+ *
+ * Each flag has a
+ *
+ * This class is immutable, and must be constructed by a {@link Builder}.
+ *
+ * @see PropertyMapper#mapIntFlag(String, int, IntFlagMapping)
+ */
+public final class IntFlagMapping {
+ private final Flag[] mFlags;
+
+ /**
+ * Get an array of the names of enabled flags for a given property value.
+ *
+ * @param value The value of the property
+ * @return The names of the enabled flags
+ */
+ @NonNull
+ public String[] namesOf(int value) {
+ ArrayList<String> enabledFlagNames = new ArrayList<>(mFlags.length);
+
+ for (Flag flag : mFlags) {
+ if (flag.isEnabledFor(value)) {
+ enabledFlagNames.add(flag.mName);
+ }
+ }
+
+ return enabledFlagNames.toArray(new String[enabledFlagNames.size()]);
+ }
+
+ /**
+ * Create a new instance from a builder.
+ *
+ * This constructor is private, use {@link Builder#build()} instead.
+ *
+ * @param builder A builder to create from
+ */
+ private IntFlagMapping(Builder builder) {
+ mFlags = builder.mFlags.toArray(new Flag[builder.mFlags.size()]);
+ }
+
+ /**
+ * A builder for {@link IntFlagMapping}.
+ */
+ public static final class Builder {
+ private ArrayList<Flag> mFlags;
+
+ public Builder() {
+ mFlags = new ArrayList<>();
+ }
+
+ /**
+ * Add a new flag without a mask.
+ *
+ * The target value will be used as a mask, to handle the common case where flag values
+ * are not mutually exclusive. The flag will be considered enabled for a property value if
+ * the result of bitwise anding the target and the value equals the target, that is:
+ * {(value & target) == target}.
+ *
+ * @param name The name of the flag
+ * @param target The value to compare against
+ * @return This builder
+ */
+ @NonNull
+ public Builder addFlag(@NonNull String name, int target) {
+ mFlags.add(new Flag(name, target, target));
+ return this;
+ }
+
+ /**
+ * Add a new flag with a mask.
+ *
+ * The flag will be considered enabled for a property value if the result of bitwise anding
+ * the value and the mask equals the target, that is: {(value & mask) == target}.
+ *
+ * @param name The name of the flag
+ * @param target The value to compare against
+ * @param mask A bit mask
+ * @return This builder
+ */
+ @NonNull
+ public Builder addFlag(@NonNull String name, int target, int mask) {
+ mFlags.add(new Flag(name, target, mask));
+ return this;
+ }
+
+ /**
+ * Clear the builder, allowing for recycling.
+ */
+ public void clear() {
+ mFlags.clear();
+ }
+
+ /**
+ * Build a new {@link IntFlagMapping} from this builder.
+ *
+ * @return A new mapping
+ */
+ @NonNull
+ public IntFlagMapping build() {
+ return new IntFlagMapping(this);
+ }
+ }
+
+ /**
+ * Inner class that holds the name, mask, and target value of a flag
+ */
+ private static final class Flag {
+ @NonNull private final String mName;
+ private final int mTarget;
+ private final int mMask;
+
+ private Flag(@NonNull String name, int target, int mask) {
+ mName = name;
+ mTarget = target;
+ mMask = mask;
+ }
+
+ /**
+ * Compare the supplied property value against the mask and taget.
+ *
+ * @param value The value to check
+ * @return True if this flag is enabled
+ */
+ private boolean isEnabledFor(int value) {
+ return (value & mMask) == mTarget;
+ }
+ }
+}
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
index 35550bd..5fb291b 100644
--- a/core/java/android/view/inspector/PropertyMapper.java
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -16,102 +16,160 @@
package android.view.inspector;
+import android.annotation.AttrRes;
import android.annotation.NonNull;
/**
* An interface for mapping the string names of inspectable properties to integer identifiers.
*
- * This interface is consumed by {@link InspectionHelper#mapProperties(PropertyMapper)}.
+ * This interface is consumed by {@link InspectionCompanion#mapProperties(PropertyMapper)}.
*
* Mapping properties to IDs enables quick comparisons against shadow copies of inspectable
* objects without performing a large number of string comparisons.
*
- * @see InspectionHelper#mapProperties(PropertyMapper)
- * @hide
+ * @see InspectionCompanion#mapProperties(PropertyMapper)
*/
public interface PropertyMapper {
/**
* Map a string name to an integer ID for a primitive boolean property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapBoolean(@NonNull String name);
+ int mapBoolean(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive byte property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapByte(@NonNull String name);
+ int mapByte(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive char property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapChar(@NonNull String name);
+ int mapChar(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive double property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapDouble(@NonNull String name);
+ int mapDouble(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive float property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapFloat(@NonNull String name);
+ int mapFloat(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive int property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapInt(@NonNull String name);
+ int mapInt(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive long property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapLong(@NonNull String name);
+ int mapLong(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive short property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapShort(@NonNull String name);
+ int mapShort(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for an object property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapObject(@NonNull String name);
+ int mapObject(@NonNull String name, @AttrRes int attributeId);
/**
+ * Map a string name to an integer ID for a color property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ * @see android.graphics.Color
+ */
+ int mapColor(@NonNull String name, @AttrRes int attributeId);
+
+ /**
+ * Map a string name to an integer ID for a gravity property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ * @see android.view.Gravity
+ */
+ int mapGravity(@NonNull String name, @AttrRes int attributeId);
+
+ /**
+ * Map a string name to an integer ID for an enumeration packed into an int property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @param mapping A mapping from int to String
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ */
+ int mapIntEnum(
+ @NonNull String name,
+ @AttrRes int attributeId,
+ @NonNull IntEnumMapping mapping);
+
+ /**
+ * Map a string name to an integer ID for a flag set packed into an int property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @param mapping A mapping from int to an array of strings
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ */
+ int mapIntFlag(
+ @NonNull String name,
+ @AttrRes int attributeId,
+ @NonNull IntFlagMapping mapping);
+ /**
* Thrown from a map method if a property name is already mapped as different type.
*/
class PropertyConflictException extends RuntimeException {
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index df81c10..fd83e8d 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -16,19 +16,21 @@
package android.view.inspector;
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Color;
/**
* An interface for reading the properties of an inspectable object.
*
- * Used as the parameter for {@link InspectionHelper#readProperties(Object, PropertyReader)}.
+ * Used as the parameter for {@link InspectionCompanion#readProperties(Object, PropertyReader)}.
* It has separate methods for all primitive types to avoid autoboxing overhead if a concrete
* implementation is able to work with primitives. Implementations should be prepared to accept
* {null} as the value of {@link PropertyReader#readObject(int, Object)}.
*
- * @see InspectionHelper#readProperties(Object, PropertyReader)
- * @hide
+ * @see InspectionCompanion#readProperties(Object, PropertyReader)
*/
public interface PropertyReader {
/**
@@ -115,6 +117,60 @@
void readObject(int id, @Nullable Object value);
/**
+ * Read a color packed into a {@link ColorInt} as a property.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+ */
+ void readColor(int id, @ColorInt int value);
+
+ /**
+ * Read a color packed into a {@link ColorLong} as a property.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+ */
+ void readColor(int id, @ColorLong long value);
+
+ /**
+ * Read a {@link Color} object as a property.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+ */
+ void readColor(int id, @Nullable Color value);
+
+ /**
+ * Read {@link android.view.Gravity} packed into an primitive {int}.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a gravity property
+ */
+ void readGravity(int id, int value);
+
+ /**
+ * Read an enumeration packed into a primitive {int}.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as an object
+ */
+ void readIntEnum(int id, int value);
+
+ /**
+ * Read a flag packed into a primitive {int}.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as an object
+ */
+ void readIntFlag(int id, int value);
+
+ /**
* Thrown if a client calls a typed read method for a property of a different type.
*/
class PropertyTypeMismatchException extends RuntimeException {
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
deleted file mode 100644
index 882fb26..0000000
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.intelligence;
-
-import android.content.ComponentName;
-import android.os.IBinder;
-import android.service.intelligence.InteractionSessionId;
-import android.view.intelligence.ContentCaptureEvent;
-
-import com.android.internal.os.IResultReceiver;
-
-import java.util.List;
-
-/**
- * {@hide}
- */
-// TODO(b/111276913): rename once the final name is defined
-oneway interface IIntelligenceManager {
- /**
- * Starts a session, sending the "remote" sessionId to the receiver.
- */
- void startSession(int userId, IBinder activityToken, in ComponentName componentName,
- in InteractionSessionId sessionId, int flags, in IResultReceiver result);
-
- /**
- * Finishes a session.
- */
- void finishSession(int userId, in InteractionSessionId sessionId,
- in List<ContentCaptureEvent> events);
-
- /**
- * Sends a batch of events
- */
- void sendEvents(int userId, in InteractionSessionId sessionId,
- in List<ContentCaptureEvent> events);
-}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 8df83c0..b41096c 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -16,7 +16,6 @@
package android.view.textclassifier;
-import android.annotation.NonNull;
import android.app.Person;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -30,6 +29,7 @@
import java.util.Deque;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -57,9 +57,9 @@
* </ul>
* User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
*/
- @NonNull
public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
- @NonNull List<ConversationActions.Message> messages) {
+ List<ConversationActions.Message> messages,
+ Function<CharSequence, String> languageDetector) {
List<ConversationActions.Message> messagesWithText =
messages.stream()
.filter(message -> !TextUtils.isEmpty(message.getText()))
@@ -67,29 +67,18 @@
if (messagesWithText.isEmpty()) {
return new ActionsSuggestionsModel.ConversationMessage[0];
}
- int size = messagesWithText.size();
- // If the last message (the most important one) does not have the Person object, we will
- // just use the last message and consider this message is sent from a remote user.
- ConversationActions.Message lastMessage = messages.get(size - 1);
- boolean useLastMessageOnly = lastMessage.getAuthor() == null;
- if (useLastMessageOnly) {
- return new ActionsSuggestionsModel.ConversationMessage[]{
- new ActionsSuggestionsModel.ConversationMessage(
- FIRST_NON_LOCAL_USER,
- lastMessage.getText().toString())};
- }
-
- // Encode the messages in the reverse order, stop whenever the Person object is missing.
Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
PersonEncoder personEncoder = new PersonEncoder();
+ int size = messagesWithText.size();
for (int i = size - 1; i >= 0; i--) {
ConversationActions.Message message = messagesWithText.get(i);
- if (message.getAuthor() == null) {
- break;
- }
+ long referenceTime = message.getReferenceTime() == null
+ ? 0
+ : message.getReferenceTime().toInstant().toEpochMilli();
nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
personEncoder.encode(message.getAuthor()),
- message.getText().toString()));
+ message.getText().toString(), referenceTime,
+ languageDetector.apply(message.getText())));
}
return nativeMessages.toArray(
new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 1a7b911..04924c9 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -30,6 +30,7 @@
import android.text.SpannedString;
import android.util.ArraySet;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -348,17 +349,31 @@
/**
* Represents the local user.
*
- * @see Builder#setAuthor(Person)
+ * @see Builder#Builder(Person)
*/
public static final Person PERSON_USER_LOCAL =
new Person.Builder()
.setKey("text-classifier-conversation-actions-local-user")
.build();
+ /**
+ * Represents the remote user.
+ * <p>
+ * If possible, you are suggested to create a {@link Person} object that can identify
+ * the remote user better, so that the underlying model could differentiate between
+ * different remote users.
+ *
+ * @see Builder#Builder(Person)
+ */
+ public static final Person PERSON_USER_REMOTE =
+ new Person.Builder()
+ .setKey("text-classifier-conversation-actions-remote-user")
+ .build();
+
@Nullable
private final Person mAuthor;
@Nullable
- private final ZonedDateTime mComposeTime;
+ private final ZonedDateTime mReferenceTime;
@Nullable
private final CharSequence mText;
@NonNull
@@ -366,18 +381,18 @@
private Message(
@Nullable Person author,
- @Nullable ZonedDateTime composeTime,
+ @Nullable ZonedDateTime referenceTime,
@Nullable CharSequence text,
@NonNull Bundle bundle) {
mAuthor = author;
- mComposeTime = composeTime;
+ mReferenceTime = referenceTime;
mText = text;
mExtras = Preconditions.checkNotNull(bundle);
}
private Message(Parcel in) {
mAuthor = in.readParcelable(null);
- mComposeTime =
+ mReferenceTime =
in.readInt() == 0
? null
: ZonedDateTime.parse(
@@ -389,9 +404,9 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mAuthor, flags);
- parcel.writeInt(mComposeTime != null ? 1 : 0);
- if (mComposeTime != null) {
- parcel.writeString(mComposeTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+ parcel.writeInt(mReferenceTime != null ? 1 : 0);
+ if (mReferenceTime != null) {
+ parcel.writeString(mReferenceTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
parcel.writeCharSequence(mText);
parcel.writeBundle(mExtras);
@@ -416,15 +431,18 @@
};
/** Returns the person that composed the message. */
- @Nullable
+ @NonNull
public Person getAuthor() {
return mAuthor;
}
- /** Returns the compose time of the message. */
+ /**
+ * Returns the reference time of the message, for example it could be the compose or send
+ * time of this message.
+ */
@Nullable
- public ZonedDateTime getTime() {
- return mComposeTime;
+ public ZonedDateTime getReferenceTime() {
+ return mReferenceTime;
}
/** Returns the text of the message. */
@@ -450,34 +468,38 @@
@Nullable
private Person mAuthor;
@Nullable
- private ZonedDateTime mComposeTime;
+ private ZonedDateTime mReferenceTime;
@Nullable
private CharSequence mText;
@Nullable
private Bundle mExtras;
/**
- * Sets the person who composed this message.
- * <p>
- * Use {@link #PERSON_USER_LOCAL} to represent the local user.
+ * Constructs a builder.
+ *
+ * @param author the person that composed the message, use {@link #PERSON_USER_LOCAL}
+ * to represent the local user. If it is not possible to identify the
+ * remote user that the local user is conversing with, use
+ * {@link #PERSON_USER_REMOTE} to represent a remote user.
*/
- @NonNull
- public Builder setAuthor(@Nullable Person author) {
- mAuthor = author;
- return this;
+ public Builder(@NonNull Person author) {
+ mAuthor = Preconditions.checkNotNull(author);
}
- /** Sets the text of this message */
+ /** Sets the text of this message. */
@NonNull
public Builder setText(@Nullable CharSequence text) {
mText = text;
return this;
}
- /** Sets the compose time of this message */
+ /**
+ * Sets the reference time of this message, for example it could be the compose or send
+ * time of this message.
+ */
@NonNull
- public Builder setComposeTime(@Nullable ZonedDateTime composeTime) {
- mComposeTime = composeTime;
+ public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
+ mReferenceTime = referenceTime;
return this;
}
@@ -493,7 +515,7 @@
public Message build() {
return new Message(
mAuthor,
- mComposeTime,
+ mReferenceTime,
mText == null ? null : new SpannedString(mText),
mExtras == null ? new Bundle() : mExtras.deepCopy());
}
@@ -654,6 +676,8 @@
@NonNull
@Hint
private final List<String> mHints;
+ @Nullable
+ private String mCallingPackageName;
private Request(
@NonNull List<Message> conversation,
@@ -666,15 +690,26 @@
mHints = hints;
}
- private Request(Parcel in) {
+ private static Request readFromParcel(Parcel in) {
List<Message> conversation = new ArrayList<>();
in.readParcelableList(conversation, null);
- mConversation = Collections.unmodifiableList(conversation);
- mTypeConfig = in.readParcelable(null);
- mMaxSuggestions = in.readInt();
+
+ TypeConfig typeConfig = in.readParcelable(null);
+
+ int maxSuggestions = in.readInt();
+
List<String> hints = new ArrayList<>();
in.readStringList(hints);
- mHints = Collections.unmodifiableList(hints);
+
+ String callingPackageName = in.readString();
+
+ Request request = new Request(
+ conversation,
+ typeConfig,
+ maxSuggestions,
+ hints);
+ request.setCallingPackageName(callingPackageName);
+ return request;
}
@Override
@@ -683,6 +718,7 @@
parcel.writeParcelable(mTypeConfig, flags);
parcel.writeInt(mMaxSuggestions);
parcel.writeStringList(mHints);
+ parcel.writeString(mCallingPackageName);
}
@Override
@@ -694,7 +730,7 @@
new Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -730,6 +766,26 @@
return mHints;
}
+ /**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
/** Builder object to construct the {@link Request} object. */
public static final class Builder {
@NonNull
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index f8fce62..c24489c 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -60,7 +60,7 @@
mSettings = Preconditions.checkNotNull(settings);
mFallback = context.getSystemService(TextClassificationManager.class)
.getTextClassifier(TextClassifier.LOCAL);
- mPackageName = Preconditions.checkNotNull(context.getPackageName());
+ mPackageName = Preconditions.checkNotNull(context.getOpPackageName());
}
/**
@@ -72,6 +72,7 @@
Preconditions.checkNotNull(request);
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextSelectionCallback callback = new TextSelectionCallback();
mManagerService.onSuggestSelection(mSessionId, request, callback);
final TextSelection selection = callback.mReceiver.get();
@@ -93,6 +94,7 @@
Preconditions.checkNotNull(request);
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextClassificationCallback callback = new TextClassificationCallback();
mManagerService.onClassifyText(mSessionId, request, callback);
final TextClassification classification = callback.mReceiver.get();
@@ -150,6 +152,7 @@
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextLanguageCallback callback = new TextLanguageCallback();
mManagerService.onDetectLanguage(mSessionId, request, callback);
final TextLanguage textLanguage = callback.mReceiver.get();
@@ -168,6 +171,7 @@
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final ConversationActionsCallback callback = new ConversationActionsCallback();
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
final ConversationActions conversationActions = callback.mReceiver.get();
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index e0910c0..d9f7965 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -37,11 +37,13 @@
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.Utils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -518,6 +520,7 @@
@Nullable private final LocaleList mDefaultLocales;
@Nullable private final ZonedDateTime mReferenceTime;
@NonNull private final Bundle mExtras;
+ @Nullable private String mCallingPackageName;
private Request(
CharSequence text,
@@ -578,6 +581,26 @@
}
/**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * For SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -660,7 +683,8 @@
*/
@NonNull
public Request build() {
- return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+ return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
+ mDefaultLocales, mReferenceTime,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
@@ -672,25 +696,37 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText.toString());
+ dest.writeCharSequence(mText);
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
- dest.writeInt(mReferenceTime != null ? 1 : 0);
- if (mReferenceTime != null) {
- dest.writeString(mReferenceTime.toString());
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+ dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final CharSequence text = in.readCharSequence();
+ final int startIndex = in.readInt();
+ final int endIndex = in.readInt();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final String referenceTimeString = in.readString();
+ final ZonedDateTime referenceTime = referenceTimeString == null
+ ? null : ZonedDateTime.parse(referenceTimeString);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, startIndex, endIndex,
+ defaultLocales, referenceTime, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -698,15 +734,6 @@
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mStartIndex = in.readInt();
- mEndIndex = in.readInt();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
- mExtras = in.readBundle();
- }
}
@Override
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index a2536cb..ea82bf3 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -162,6 +162,14 @@
TextClassifier NO_OP = new TextClassifier() {};
/**
+ * Used as a boolean value to indicate the intent is generated by TextClassifier.
+ * <p>
+ * All {@link TextClassifier} implementations should set this boolean extra to be true in their
+ * generated intents.
+ */
+ String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
+
+ /**
* Returns suggested text selection start and end indices, recognized entity types, and their
* associated confidence scores. The entity types are ordered from highest to lowest scoring.
*
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 8e14dfd..deda926 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -139,7 +139,7 @@
FACTORY_MODEL_DIR,
LANG_ID_FACTORY_MODEL_FILENAME_REGEX,
UPDATED_LANG_ID_MODEL_FILE,
- fd -> -1, // TODO: Replace this with LangIdModel.getVersion(fd)
+ LangIdModel::getVersion,
fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT));
mActionsModelFileManager = new ModelFileManager(
new ModelFileManager.ModelFileSupplierImpl(
@@ -374,7 +374,8 @@
return mFallback.suggestConversationActions(request);
}
ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
- ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
+ ActionsSuggestionsHelper.toNativeMessages(request.getConversation(),
+ this::detectLanguageTagsFromText);
if (nativeMessages.length == 0) {
return mFallback.suggestConversationActions(request);
}
@@ -407,6 +408,26 @@
return mFallback.suggestConversationActions(request);
}
+ @Nullable
+ private String detectLanguageTagsFromText(CharSequence text) {
+ TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+ TextLanguage textLanguage = detectLanguage(request);
+ int localeHypothesisCount = textLanguage.getLocaleHypothesisCount();
+ List<String> languageTags = new ArrayList<>();
+ // TODO: Reconsider this and probably make the score threshold configurable.
+ for (int i = 0; i < localeHypothesisCount; i++) {
+ ULocale locale = textLanguage.getLocale(i);
+ if (textLanguage.getConfidenceScore(locale) < 0.5) {
+ break;
+ }
+ languageTags.add(locale.toLanguageTag());
+ }
+ if (languageTags.isEmpty()) {
+ return LocaleList.getDefault().toLanguageTags();
+ }
+ return String.join(",", languageTags);
+ }
+
private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
List<String> defaultActionTypes =
request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
@@ -777,6 +798,9 @@
if (foreignText) {
insertTranslateAction(actions, context, text);
}
+ actions.forEach(
+ action -> action.getIntent()
+ .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
return actions;
}
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index d28459e..b1609fc 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -26,6 +26,7 @@
import android.os.Parcelable;
import android.util.ArrayMap;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.Locale;
@@ -90,7 +91,7 @@
* confidence to low confidence.
*
* @throws IndexOutOfBoundsException if the specified index is out of range.
- * @see #getLocaleCount() for the number of locales available.
+ * @see #getLocaleHypothesisCount() for the number of locales available.
*/
@NonNull
public ULocale getLocale(int index) {
@@ -222,11 +223,12 @@
};
private final CharSequence mText;
- private final Bundle mBundle;
+ private final Bundle mExtra;
+ @Nullable private String mCallingPackageName;
private Request(CharSequence text, Bundle bundle) {
mText = text;
- mBundle = bundle;
+ mExtra = bundle;
}
/**
@@ -238,6 +240,25 @@
}
/**
+ * Sets the name of the package that is sending this request.
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns null if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns a bundle containing non-structured extra information about this request.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -246,7 +267,7 @@
*/
@NonNull
public Bundle getExtras() {
- return mBundle.deepCopy();
+ return mExtra.deepCopy();
}
@Override
@@ -257,13 +278,18 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mText);
- dest.writeBundle(mBundle);
+ dest.writeString(mCallingPackageName);
+ dest.writeBundle(mExtra);
}
private static Request readFromParcel(Parcel in) {
- return new Request(
- in.readCharSequence(),
- in.readBundle());
+ final CharSequence text = in.readCharSequence();
+ final String callingPackageName = in.readString();
+ final Bundle extra = in.readBundle();
+
+ final Request request = new Request(text, extra);
+ request.setCallingPackageName(callingPackageName);
+ return request;
}
/**
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1e42c41..ab34178 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -30,6 +30,7 @@
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.view.View;
+import android.view.textclassifier.TextClassifier.EntityConfig;
import android.view.textclassifier.TextClassifier.EntityType;
import android.widget.TextView;
@@ -205,27 +206,32 @@
private final EntityConfidence mEntityScores;
private final int mStart;
private final int mEnd;
- @Nullable final URLSpan mUrlSpan;
+ private final Bundle mExtras;
+ @Nullable private final URLSpan mUrlSpan;
/**
* Create a new TextLink.
*
* @param start The start index of the identified subsequence
* @param end The end index of the identified subsequence
- * @param entityScores A mapping of entity type to confidence score
+ * @param entityConfidence A mapping of entity type to confidence score
+ * @param extras A bundle containing custom data related to this TextLink
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
*
- * @throws IllegalArgumentException if entityScores is null or empty
+ * @throws IllegalArgumentException if {@code entityConfidence} is null or empty
+ * @throws IllegalArgumentException if {@code start} is greater than {@code end}
*/
- TextLink(int start, int end, Map<String, Float> entityScores,
- @Nullable URLSpan urlSpan) {
- Preconditions.checkNotNull(entityScores);
- Preconditions.checkArgument(!entityScores.isEmpty());
+ private TextLink(int start, int end, @NonNull EntityConfidence entityConfidence,
+ @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+ Preconditions.checkNotNull(entityConfidence);
+ Preconditions.checkArgument(!entityConfidence.getEntities().isEmpty());
Preconditions.checkArgument(start <= end);
+ Preconditions.checkNotNull(extras);
mStart = start;
mEnd = end;
- mEntityScores = new EntityConfidence(entityScores);
+ mEntityScores = entityConfidence;
mUrlSpan = urlSpan;
+ mExtras = extras;
}
/**
@@ -274,6 +280,13 @@
return mEntityScores.getConfidenceScore(entityType);
}
+ /**
+ * Returns a bundle containing custom data related to this TextLink.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public String toString() {
return String.format(Locale.US,
@@ -291,13 +304,22 @@
mEntityScores.writeToParcel(dest, flags);
dest.writeInt(mStart);
dest.writeInt(mEnd);
+ dest.writeBundle(mExtras);
+ }
+
+ private static TextLink readFromParcel(Parcel in) {
+ final EntityConfidence entityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+ final int start = in.readInt();
+ final int end = in.readInt();
+ final Bundle extras = in.readBundle();
+ return new TextLink(start, end, entityConfidence, extras, null /* urlSpan */);
}
public static final Parcelable.Creator<TextLink> CREATOR =
new Parcelable.Creator<TextLink>() {
@Override
public TextLink createFromParcel(Parcel in) {
- return new TextLink(in);
+ return readFromParcel(in);
}
@Override
@@ -305,13 +327,6 @@
return new TextLink[size];
}
};
-
- private TextLink(Parcel in) {
- mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
- mStart = in.readInt();
- mEnd = in.readInt();
- mUrlSpan = null;
- }
}
/**
@@ -321,23 +336,21 @@
private final CharSequence mText;
@Nullable private final LocaleList mDefaultLocales;
- @Nullable private final TextClassifier.EntityConfig mEntityConfig;
+ @Nullable private final EntityConfig mEntityConfig;
private final boolean mLegacyFallback;
- private String mCallingPackageName;
+ @Nullable private String mCallingPackageName;
private final Bundle mExtras;
private Request(
CharSequence text,
LocaleList defaultLocales,
- TextClassifier.EntityConfig entityConfig,
+ EntityConfig entityConfig,
boolean legacyFallback,
- String callingPackageName,
Bundle extras) {
mText = text;
mDefaultLocales = defaultLocales;
mEntityConfig = entityConfig;
mLegacyFallback = legacyFallback;
- mCallingPackageName = callingPackageName;
mExtras = extras;
}
@@ -360,10 +373,10 @@
/**
* @return The config representing the set of entities to look for
- * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
+ * @see Builder#setEntityConfig(EntityConfig)
*/
@Nullable
- public TextClassifier.EntityConfig getEntityConfig() {
+ public EntityConfig getEntityConfig() {
return mEntityConfig;
}
@@ -378,13 +391,26 @@
}
/**
- * Sets the name of the package that requested the links to get generated.
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
*/
- void setCallingPackageName(@Nullable String callingPackageName) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
mCallingPackageName = callingPackageName;
}
/**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -404,9 +430,8 @@
private final CharSequence mText;
@Nullable private LocaleList mDefaultLocales;
- @Nullable private TextClassifier.EntityConfig mEntityConfig;
+ @Nullable private EntityConfig mEntityConfig;
private boolean mLegacyFallback = true; // Use legacy fall back by default.
- private String mCallingPackageName;
@Nullable private Bundle mExtras;
public Builder(@NonNull CharSequence text) {
@@ -434,7 +459,7 @@
* @return this builder
*/
@NonNull
- public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+ public Builder setEntityConfig(@Nullable EntityConfig entityConfig) {
mEntityConfig = entityConfig;
return this;
}
@@ -455,18 +480,6 @@
}
/**
- * Sets the name of the package that requested the links to get generated.
- *
- * @return this builder
- * @hide
- */
- @NonNull
- public Builder setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- return this;
- }
-
- /**
* Sets the extended data.
*
* @return this builder
@@ -483,21 +496,11 @@
public Request build() {
return new Request(
mText, mDefaultLocales, mEntityConfig,
- mLegacyFallback, mCallingPackageName,
+ mLegacyFallback,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
- /**
- * @return the name of the package that requested the links to get generated.
- * TODO: make available as system API
- * @hide
- */
- @Nullable
- public String getCallingPackageName() {
- return mCallingPackageName;
- }
-
@Override
public int describeContents() {
return 0;
@@ -506,23 +509,30 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mText.toString());
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
- dest.writeInt(mEntityConfig != null ? 1 : 0);
- if (mEntityConfig != null) {
- mEntityConfig.writeToParcel(dest, flags);
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeParcelable(mEntityConfig, flags);
dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final String text = in.readString();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final EntityConfig entityConfig = in.readParcelable(null);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, defaultLocales, entityConfig,
+ /* legacyFallback= */ true, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -530,16 +540,6 @@
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mEntityConfig = in.readInt() == 0
- ? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
- mLegacyFallback = true;
- mCallingPackageName = in.readString();
- mExtras = in.readBundle();
- }
}
/**
@@ -645,9 +645,20 @@
* @throws IllegalArgumentException if entityScores is null or empty.
*/
@NonNull
- public Builder addLink(int start, int end, Map<String, Float> entityScores) {
- mLinks.add(new TextLink(start, end, entityScores, null));
- return this;
+ public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores) {
+ return addLink(start, end, entityScores, Bundle.EMPTY, null);
+ }
+
+ /**
+ * Adds a TextLink.
+ *
+ * @see #addLink(int, int, Map)
+ * @param extras An optional bundle containing custom data related to this TextLink
+ */
+ @NonNull
+ public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+ @NonNull Bundle extras) {
+ return addLink(start, end, entityScores, extras, null);
}
/**
@@ -655,9 +666,15 @@
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
*/
@NonNull
- Builder addLink(int start, int end, Map<String, Float> entityScores,
+ Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
@Nullable URLSpan urlSpan) {
- mLinks.add(new TextLink(start, end, entityScores, urlSpan));
+ return addLink(start, end, entityScores, Bundle.EMPTY, urlSpan);
+ }
+
+ private Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+ @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+ mLinks.add(new TextLink(
+ start, end, new EntityConfidence(entityScores), extras, urlSpan));
return this;
}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index f236915..4a6f3e5 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -24,10 +24,12 @@
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.Utils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.Locale;
@@ -209,6 +211,7 @@
@Nullable private final LocaleList mDefaultLocales;
private final boolean mDarkLaunchAllowed;
private final Bundle mExtras;
+ @Nullable private String mCallingPackageName;
private Request(
CharSequence text,
@@ -270,6 +273,26 @@
}
/**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -355,7 +378,7 @@
*/
@NonNull
public Request build() {
- return new Request(mText, mStartIndex, mEndIndex,
+ return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
mDefaultLocales, mDarkLaunchAllowed,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
@@ -368,21 +391,33 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText.toString());
+ dest.writeCharSequence(mText);
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final CharSequence text = in.readCharSequence();
+ final int startIndex = in.readInt();
+ final int endIndex = in.readInt();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, startIndex, endIndex, defaultLocales,
+ /* darkLaunchAllowed= */ false, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -390,15 +425,6 @@
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mStartIndex = in.readInt();
- mEndIndex = in.readInt();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mDarkLaunchAllowed = false;
- mExtras = in.readBundle();
- }
}
@Override
diff --git a/core/java/android/webkit/TokenBindingService.java b/core/java/android/webkit/TokenBindingService.java
index b37e1b8..4d2c046 100644
--- a/core/java/android/webkit/TokenBindingService.java
+++ b/core/java/android/webkit/TokenBindingService.java
@@ -16,12 +16,7 @@
package android.webkit;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.net.Uri;
-
-import java.security.KeyPair;
/**
* Enables the token binding procotol, and provides access to the keys. See
@@ -30,86 +25,9 @@
* All methods are required to be called on the UI thread where WebView is
* attached to the View hierarchy.
* @hide
+ * @deprecated this is no longer supported.
*/
@SystemApi
+@Deprecated
public abstract class TokenBindingService {
-
- public static final String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5";
- public static final String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS";
- public static final String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256";
-
- /**
- * Provides the KeyPair information.
- */
- public static abstract class TokenBindingKey {
- /**
- * The public, private key pair.
- */
- public abstract KeyPair getKeyPair();
-
- /**
- * The algorithm that is used to generate the key pair.
- */
- public abstract String getAlgorithm();
- }
-
- /**
- * Returns the default TokenBinding service instance. At present there is
- * only one token binding service instance for all WebView instances,
- * however this restriction may be relaxed in the future.
- *
- * @return The default TokenBindingService instance.
- */
- public static TokenBindingService getInstance() {
- return WebViewFactory.getProvider().getTokenBindingService();
- }
-
- /**
- * Enables the token binding protocol. The token binding protocol
- * has to be enabled before creating any WebViews.
- *
- * @throws IllegalStateException if a WebView was already created.
- */
- public abstract void enableTokenBinding();
-
- /**
- * Retrieves the key pair for a given origin from the internal
- * TokenBinding key store asynchronously.
- *
- * The user can provide a list of acceptable algorithms for the retrieved
- * key pair. If a key pair exists and it is in the list of algorithms, then
- * the key is returned. If it is not in the list, no key is returned.
- *
- * If no key pair exists, WebView chooses an algorithm from the list, in
- * the order given, to generate a key.
- *
- * The user can pass {@code null} if any algorithm is acceptable.
- *
- * @param origin The origin for the server.
- * @param algorithm The list of algorithms. An IllegalArgumentException is thrown if array is
- * empty.
- * @param callback The callback that will be called when key is available.
- */
- public abstract void getKey(Uri origin,
- @Nullable String[] algorithm,
- @NonNull ValueCallback<TokenBindingKey> callback);
- /**
- * Deletes specified key (for use when associated cookie is cleared).
- *
- * @param origin The origin of the server.
- * @param callback The callback that will be called when key is deleted. The
- * callback parameter (Boolean) will indicate if operation is
- * successful or if failed.
- */
- public abstract void deleteKey(Uri origin,
- @Nullable ValueCallback<Boolean> callback);
-
- /**
- * Deletes all the keys (for use when cookies are cleared).
- *
- * @param callback The callback that will be called when keys are deleted.
- * The callback parameter (Boolean) will indicate if operation is
- * successful or if failed.
- */
- public abstract void deleteAllKeys(@Nullable ValueCallback<Boolean> callback);
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index c30edd3..8b98469 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -224,6 +224,42 @@
*/
public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2;
+ /** @hide */
+ @IntDef(prefix = { "FORCE_DARK_" }, value = {
+ FORCE_DARK_OFF,
+ FORCE_DARK_AUTO,
+ FORCE_DARK_ON
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ForceDarkMode {}
+
+ /**
+ * Used with {@link #setForceDarkMode}
+ *
+ * Disable force dark, irrespective of the force dark mode of the WebView parent. In this mode,
+ * WebView content will always be rendered as-is, regardless of whether native views are being
+ * automatically darkened.
+ */
+ public static final int FORCE_DARK_OFF = -1;
+
+ /**
+ * Used with {@link #setForceDarkMode}
+ *
+ * Enable force dark, dependent on the state of the WebView parent. If the WebView parent view
+ * is being automatically rendered in dark mode, then WebView content will be rendered so as to
+ * emulate a dark theme. WebViews that are not attached to the view hierarchy will not be
+ * inverted.
+ */
+ public static final int FORCE_DARK_AUTO = 0;
+
+ /**
+ * Used with {@link #setForceDarkMode}
+ *
+ * Unconditionally enable force dark. In this mode WebView content will always be rendered so
+ * as to emulate a dark theme.
+ */
+ public static final int FORCE_DARK_ON = +1;
+
/**
* Enables dumping the pages navigation cache to a text file. The default
* is {@code false}.
@@ -1429,6 +1465,23 @@
/**
+ * Set the force dark mode for this WebView.
+ */
+ public void setForceDarkMode(@ForceDarkMode int forceDarkMode) {
+ // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
+ }
+
+ /**
+ * Get the force dark mode for this WebView.
+ *
+ * @return the currently set force dark mode.
+ */
+ public @ForceDarkMode int getForceDarkMode() {
+ // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
+ return FORCE_DARK_AUTO;
+ }
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "MENU_ITEM_" }, value = {
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 4ff49ea..6a1ed39 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -129,7 +129,8 @@
* Gets the TokenBindingService instance for this WebView implementation. The
* implementation must return the same instance on subsequent calls.
*
- * @return the TokenBindingService instance
+ * @deprecated this method only returns {@code null}
+ * @return the TokenBindingService instance (which is always {@code null})
*/
TokenBindingService getTokenBindingService();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c6155ce..3c32bb2 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2051,8 +2051,8 @@
}
void updateCursorPosition() {
- if (mTextView.mCursorDrawableRes == 0) {
- mDrawableForCursor = null;
+ loadCursorDrawable();
+ if (mDrawableForCursor == null) {
return;
}
@@ -2462,10 +2462,7 @@
}
private void updateCursorPosition(int top, int bottom, float horizontal) {
- if (mDrawableForCursor == null) {
- mDrawableForCursor = mTextView.getContext().getDrawable(
- mTextView.mCursorDrawableRes);
- }
+ loadCursorDrawable();
final int left = clampHorizontalPosition(mDrawableForCursor, horizontal);
final int width = mDrawableForCursor.getIntrinsicWidth();
mDrawableForCursor.setBounds(left, top - mTempRect.top, left + width,
@@ -5698,6 +5695,12 @@
public boolean isActive();
}
+ void loadCursorDrawable() {
+ if (mDrawableForCursor == null) {
+ mDrawableForCursor = mTextView.getTextCursorDrawable();
+ }
+ }
+
private class InsertionPointCursorController implements CursorController {
private InsertionHandleView mHandle;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7b39efe..2dec4e8 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -18,6 +18,8 @@
import android.annotation.ColorInt;
import android.annotation.DimenRes;
+import android.annotation.IntDef;
+import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.StyleRes;
import android.annotation.UnsupportedAppUsage;
@@ -131,6 +133,12 @@
static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
/**
+ * The intent extra that contains {@code true} if inflating as dak text theme.
+ * @hide
+ */
+ static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
+
+ /**
* The intent extra that contains the bounds for all shared elements.
*/
public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
@@ -163,6 +171,36 @@
private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
private static final int SET_INT_TAG_TAG = 22;
+ /** @hide **/
+ @IntDef(flag = true, value = {
+ FLAG_REAPPLY_DISALLOWED,
+ FLAG_WIDGET_IS_COLLECTION_CHILD,
+ FLAG_USE_LIGHT_BACKGROUND_LAYOUT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApplyFlags {}
+ /**
+ * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
+ * the layout in a way that isn't recoverable, since views are being removed.
+ * @hide
+ */
+ public static final int FLAG_REAPPLY_DISALLOWED = 1;
+ /**
+ * This flag indicates whether this RemoteViews object is being created from a
+ * RemoteViewsService for use as a child of a widget collection. This flag is used
+ * to determine whether or not certain features are available, in particular,
+ * setting on click extras and setting on click pending intents. The former is enabled,
+ * and the latter disabled when this flag is true.
+ * @hide
+ */
+ public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
+ /**
+ * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
+ * of {link #mLayoutId}
+ * @hide
+ */
+ public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
+
/**
* Application that hosts the remote views.
*
@@ -178,6 +216,11 @@
private final int mLayoutId;
/**
+ * The resource ID of the layout file in dark text mode. (Added to the parcel)
+ */
+ private int mLightBackgroundLayoutId = 0;
+
+ /**
* An array of actions to perform on the view tree once it has been
* inflated
*/
@@ -197,12 +240,6 @@
private boolean mIsRoot = true;
/**
- * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
- * the layout in a way that isn't recoverable, since views are being removed.
- */
- private boolean mReapplyDisallowed;
-
- /**
* Constants to whether or not this RemoteViews is composed of a landscape and portrait
* RemoteViews.
*/
@@ -218,14 +255,8 @@
@UnsupportedAppUsage
private RemoteViews mPortrait = null;
- /**
- * This flag indicates whether this RemoteViews object is being created from a
- * RemoteViewsService for use as a child of a widget collection. This flag is used
- * to determine whether or not certain features are available, in particular,
- * setting on click extras and setting on click pending intents. The former is enabled,
- * and the latter disabled when this flag is true.
- */
- private boolean mIsWidgetCollectionChild = false;
+ @ApplyFlags
+ private int mApplyFlags = 0;
/** Class cookies of the Parcel this instance was read from. */
private final Map<Class, Object> mClassCookies;
@@ -289,18 +320,15 @@
*
* @hide
*/
- public void setReapplyDisallowed() {
- mReapplyDisallowed = true;
+ public void addFlags(@ApplyFlags int flags) {
+ mApplyFlags = mApplyFlags | flags;
}
/**
- * @return Whether it is disallowed to reapply another remoteview with the same layout as this
- * view. True if this remoteview has actions that destroyed view tree of the base layout.
- *
* @hide
*/
- public boolean isReapplyDisallowed() {
- return mReapplyDisallowed;
+ public boolean hasFlags(@ApplyFlags int flag) {
+ return (mApplyFlags & flag) == flag;
}
/**
@@ -768,7 +796,10 @@
// Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
// RemoteViewsService
AppWidgetHostView host = (AppWidgetHostView) rootParent;
- intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
+ intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
+ .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
+ hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
+
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
v.setRemoteViewsAdapter(intent, isAsync);
@@ -829,7 +860,7 @@
// If the view is an AdapterView, setting a PendingIntent on click doesn't make
// much sense, do they mean to set a PendingIntent template for the
// AdapterView's children?
- if (mIsWidgetCollectionChild) {
+ if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
+ "(id: " + viewId + ")");
ApplicationInfo appInfo = root.getContext().getApplicationInfo();
@@ -843,7 +874,7 @@
}
target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
} else if (mResponse.mFillIntent != null) {
- if (!mIsWidgetCollectionChild) {
+ if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
+ "only from RemoteViewsFactory (ie. on collection items).");
return;
@@ -1545,6 +1576,7 @@
viewId = parcel.readInt();
mIndex = parcel.readInt();
mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
+ mNestedViews.addFlags(mApplyFlags);
}
public void writeToParcel(Parcel dest, int flags) {
@@ -2190,7 +2222,7 @@
*
* @hide
*/
- public RemoteViews(String packageName, int userId, int layoutId) {
+ public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
this(getApplicationInfo(packageName, userId), layoutId);
}
@@ -2203,7 +2235,7 @@
*
* @hide
*/
- protected RemoteViews(ApplicationInfo application, int layoutId) {
+ protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
mApplication = application;
mLayoutId = layoutId;
mBitmapCache = new BitmapCache();
@@ -2229,7 +2261,8 @@
throw new RuntimeException("Both RemoteViews must share the same package and user");
}
mApplication = portrait.mApplication;
- mLayoutId = portrait.getLayoutId();
+ mLayoutId = portrait.mLayoutId;
+ mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
mLandscape = landscape;
mPortrait = portrait;
@@ -2250,8 +2283,8 @@
mApplication = src.mApplication;
mIsRoot = src.mIsRoot;
mLayoutId = src.mLayoutId;
- mIsWidgetCollectionChild = src.mIsWidgetCollectionChild;
- mReapplyDisallowed = src.mReapplyDisallowed;
+ mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
+ mApplyFlags = src.mApplyFlags;
mClassCookies = src.mClassCookies;
if (src.hasLandscapeAndPortraitLayouts()) {
@@ -2309,7 +2342,7 @@
mApplication = parcel.readInt() == 0 ? info :
ApplicationInfo.CREATOR.createFromParcel(parcel);
mLayoutId = parcel.readInt();
- mIsWidgetCollectionChild = parcel.readInt() == 1;
+ mLightBackgroundLayoutId = parcel.readInt();
readActionsFromParcel(parcel, depth);
} else {
@@ -2318,9 +2351,10 @@
mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
mClassCookies);
mApplication = mPortrait.mApplication;
- mLayoutId = mPortrait.getLayoutId();
+ mLayoutId = mPortrait.mLayoutId;
+ mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
}
- mReapplyDisallowed = parcel.readInt() == 0;
+ mApplyFlags = parcel.readInt();
}
private void readActionsFromParcel(Parcel parcel, int depth) {
@@ -2409,19 +2443,8 @@
* @return the layout id.
*/
public int getLayoutId() {
- return mLayoutId;
- }
-
- /*
- * This flag indicates whether this RemoteViews object is being created from a
- * RemoteViewsService for use as a child of a widget collection. This flag is used
- * to determine whether or not certain features are available, in particular,
- * setting on click extras and setting on click pending intents. The former is enabled,
- * and the latter disabled when this flag is true.
- */
- @UnsupportedAppUsage
- void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
- mIsWidgetCollectionChild = isWidgetCollectionChild;
+ return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
+ ? mLightBackgroundLayoutId : mLayoutId;
}
/**
@@ -3292,6 +3315,33 @@
setInt(viewId, "setLabelFor", labeledId);
}
+ /**
+ * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
+ * used by the host when the widgets displayed on a light-background where foreground elements
+ * and text can safely draw using a dark color without any additional background protection.
+ */
+ public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
+ mLightBackgroundLayoutId = layoutId;
+ }
+
+ /**
+ * If this view supports dark text versions, creates a copy representing that version,
+ * otherwise returns itself.
+ * @hide
+ */
+ public RemoteViews getDarkTextViews() {
+ if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
+ return this;
+ }
+
+ try {
+ addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+ return new RemoteViews(this);
+ } finally {
+ mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
+ }
+ }
+
private RemoteViews getRemoteViewsToApply(Context context) {
if (hasLandscapeAndPortraitLayouts()) {
int orientation = context.getResources().getConfiguration().orientation;
@@ -3373,6 +3423,12 @@
* @hide
*/
public interface OnViewAppliedListener {
+ /**
+ * Callback when the RemoteView has finished inflating,
+ * but no actions have been applied yet.
+ */
+ default void onViewInflated(View v) {};
+
void onViewApplied(View v);
void onError(Exception e);
@@ -3469,6 +3525,10 @@
@Override
protected void onPostExecute(ViewTree viewTree) {
if (mError == null) {
+ if (mListener != null) {
+ mListener.onViewInflated(viewTree.mRoot);
+ }
+
try {
if (mActions != null) {
OnClickHandler handler = mHandler == null
@@ -3652,7 +3712,7 @@
mApplication.writeToParcel(dest, flags);
}
dest.writeInt(mLayoutId);
- dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
+ dest.writeInt(mLightBackgroundLayoutId);
writeActionsToParcel(dest);
} else {
dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
@@ -3665,7 +3725,7 @@
// Both RemoteViews already share the same package and user
mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
}
- dest.writeInt(mReapplyDisallowed ? 1 : 0);
+ dest.writeInt(mApplyFlags);
}
private void writeActionsToParcel(Parcel parcel) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index d17c7c5..365638f 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -16,6 +16,9 @@
package android.widget;
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
+
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.IServiceConnection;
@@ -97,6 +100,7 @@
private final Context mContext;
private final Intent mIntent;
private final int mAppWidgetId;
+ private final boolean mOnLightBackground;
private final Executor mAsyncViewLoadExecutor;
private OnClickHandler mRemoteViewsOnClickHandler;
@@ -249,8 +253,12 @@
final IServiceConnection sd = mContext.getServiceDispatcher(this, this, flags);
Intent intent = (Intent) msg.obj;
int appWidgetId = msg.arg1;
- mBindRequested = AppWidgetManager.getInstance(mContext)
- .bindRemoteViewsService(mContext, appWidgetId, intent, sd, flags);
+ try {
+ mBindRequested = AppWidgetManager.getInstance(mContext)
+ .bindRemoteViewsService(mContext, appWidgetId, intent, sd, flags);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to bind remoteViewsService: " + e.getMessage());
+ }
return;
}
case MSG_NOTIFY_DATA_SET_CHANGED: {
@@ -817,13 +825,13 @@
throw new IllegalArgumentException("Non-null Intent must be specified.");
}
- mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+ mAppWidgetId = intent.getIntExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
+ mOnLightBackground = intent.getBooleanExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, false);
// Strip the previously injected app widget id from service intent
- if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
- intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
- }
+ intent.removeExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID);
+ intent.removeExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND);
// Initialize the worker thread
mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
@@ -1107,6 +1115,7 @@
} else {
layout = new RemoteViewsFrameLayout(parent.getContext(), mCache);
layout.setExecutor(mAsyncViewLoadExecutor);
+ layout.setOnLightBackground(mOnLightBackground);
}
if (isInCache) {
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index e490458..b80fe48 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -85,7 +85,7 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (position < getCount()) {
RemoteViews rv = mRemoteViewsList.get(position);
- rv.setIsWidgetCollectionChild(true);
+ rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
View v;
if (convertView != null && rv != null &&
convertView.getId() == rv.getLayoutId()) {
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 2827f63..214e5cc 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -163,7 +163,7 @@
try {
rv = mFactory.getViewAt(position);
if (rv != null) {
- rv.setIsWidgetCollectionChild(true);
+ rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
}
} catch (Exception ex) {
Thread t = Thread.currentThread();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 085f8f1..90da812 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -157,6 +157,7 @@
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.view.contentcapture.ContentCaptureManager;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -166,7 +167,6 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.intelligence.ContentCaptureManager;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
@@ -799,8 +799,9 @@
// Although these fields are specific to editable text, they are not added to Editor because
// they are defined by the TextView's style and are theme-dependent.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
int mCursorDrawableRes;
+ private Drawable mCursorDrawable;
// Note: this might be stale if setTextSelectHandleLeft is used. We could simplify the code
// by removing it, but we would break apps targeting <= P that use it by reflection.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -3640,6 +3641,8 @@
/**
* Returns the Drawable corresponding to the right handle used
* for selecting text.
+ * Note that any change applied to the handle Drawable will not be visible
+ * until the handle is hidden and then drawn again.
*
* @return the right text selection handle drawable
*
@@ -3655,6 +3658,58 @@
}
/**
+ * Sets the Drawable corresponding to the text cursor. The Drawable defaults to the
+ * value of the textCursorDrawable attribute.
+ * Note that any change applied to the cursor Drawable will not be visible
+ * until the cursor is hidden and then drawn again.
+ *
+ * @see #setTextCursorDrawable(int)
+ * @attr ref android.R.styleable#TextView_textCursorDrawable
+ */
+ public void setTextCursorDrawable(@NonNull Drawable textCursorDrawable) {
+ Preconditions.checkNotNull(textCursorDrawable,
+ "The cursor drawable should not be null.");
+ mCursorDrawable = textCursorDrawable;
+ mCursorDrawableRes = 0;
+ if (mEditor != null) {
+ mEditor.loadCursorDrawable();
+ }
+ }
+
+ /**
+ * Sets the Drawable corresponding to the text cursor. The Drawable defaults to the
+ * value of the textCursorDrawable attribute.
+ * Note that any change applied to the cursor Drawable will not be visible
+ * until the cursor is hidden and then drawn again.
+ *
+ * @see #setTextCursorDrawable(Drawable)
+ * @attr ref android.R.styleable#TextView_textCursorDrawable
+ */
+ public void setTextCursorDrawable(@DrawableRes int textCursorDrawable) {
+ Preconditions.checkArgumentPositive(textCursorDrawable,
+ "The cursor drawable should be a valid drawable resource id.");
+ setTextCursorDrawable(mContext.getDrawable(textCursorDrawable));
+ }
+
+ /**
+ * Returns the Drawable corresponding to the text cursor.
+ * Note that any change applied to the cursor Drawable will not be visible
+ * until the cursor is hidden and then drawn again.
+ *
+ * @return the text cursor drawable
+ *
+ * @see #setTextCursorDrawable(Drawable)
+ * @see #setTextCursorDrawable(int)
+ * @attr ref android.R.styleable#TextView_textCursorDrawable
+ */
+ @Nullable public Drawable getTextCursorDrawable() {
+ if (mCursorDrawable == null && mCursorDrawableRes != 0) {
+ mCursorDrawable = mContext.getDrawable(mCursorDrawableRes);
+ }
+ return mCursorDrawable;
+ }
+
+ /**
* Sets the text appearance from the specified style resource.
* <p>
* Use a framework-defined {@code TextAppearance} style like
@@ -5973,14 +6028,22 @@
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
- if (!precomputed.getParams().isSameTextMetricsInternal(
- getPaint(), mTextDir, mBreakStrategy, mHyphenationFrequency)) {
- throw new IllegalArgumentException(
+ final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
+ precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
+ mHyphenationFrequency);
+ switch (checkResult) {
+ case PrecomputedText.Params.UNUSABLE:
+ throw new IllegalArgumentException(
"PrecomputedText's Parameters don't match the parameters of this TextView."
+ "Consider using setTextMetricsParams(precomputedText.getParams()) "
+ "to override the settings of this TextView: "
+ "PrecomputedText: " + precomputed.getParams()
+ "TextView: " + getTextMetricsParams());
+ case PrecomputedText.Params.NEED_RECOMPUTE:
+ precomputed = PrecomputedText.create(precomputed, getTextMetricsParams());
+ break;
+ case PrecomputedText.Params.USABLE:
+ // pass through
}
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
@@ -10154,7 +10217,7 @@
}
// ContentCapture
- if (isImportantForContentCapture() && isTextEditable()) {
+ if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) {
final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
if (cm != null && cm.isContentCaptureEnabled()) {
// TODO(b/111276913): pass flags when edited by user / add CTS test
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 46c6fa4..ef5eb6c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -94,7 +94,7 @@
public class ResolverActivity extends Activity {
// Temporary flag for new chooser delegate behavior.
- boolean mEnableChooserDelegate = false;
+ boolean mEnableChooserDelegate = true;
protected ResolveListAdapter mAdapter;
private boolean mSafeForwardingMode;
@@ -887,6 +887,8 @@
chooserIntent.putExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY,
ignoreTargetSecurity);
chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId);
+ chooserIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
+ | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
startActivity(chooserIntent);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 8bc90a8..a27dbea 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -39,6 +39,7 @@
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.MetadataReader;
+import android.system.Int64Ref;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -53,6 +54,12 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -122,18 +129,56 @@
throw new FileNotFoundException("Can't find the file for documentId: " + documentId);
}
+ final String mimeType = getDocumentType(documentId);
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ final Int64Ref treeCount = new Int64Ref(0);
+ final Int64Ref treeSize = new Int64Ref(0);
+ try {
+ final Path path = FileSystems.getDefault().getPath(file.getAbsolutePath());
+ Files.walkFileTree(path, new FileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ treeCount.value += 1;
+ treeSize.value += attrs.size();
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ Log.e(TAG, "An error occurred retrieving the metadata", e);
+ return null;
+ }
+
+ final Bundle res = new Bundle();
+ res.putLong(DocumentsContract.METADATA_TREE_COUNT, treeCount.value);
+ res.putLong(DocumentsContract.METADATA_TREE_SIZE, treeSize.value);
+ return res;
+ }
+
if (!file.isFile()) {
Log.w(TAG, "Can't stream non-regular file. Returning empty metadata.");
return null;
}
-
if (!file.canRead()) {
Log.w(TAG, "Can't stream non-readable file. Returning empty metadata.");
return null;
}
-
- String mimeType = getDocumentType(documentId);
if (!MetadataReader.isSupportedMimeType(mimeType)) {
+ Log.w(TAG, "Unsupported type " + mimeType + ". Returning empty metadata.");
return null;
}
@@ -562,7 +607,8 @@
}
protected boolean typeSupportsMetadata(String mimeType) {
- return MetadataReader.isSupportedMimeType(mimeType);
+ return MetadataReader.isSupportedMimeType(mimeType)
+ || Document.MIME_TYPE_DIR.equals(mimeType);
}
protected final File getFileForDocId(String docId) throws FileNotFoundException {
diff --git a/core/java/com/android/internal/os/AppIdToPackageMap.java b/core/java/com/android/internal/os/AppIdToPackageMap.java
new file mode 100644
index 0000000..65aa989
--- /dev/null
+++ b/core/java/com/android/internal/os/AppIdToPackageMap.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+
+import android.app.AppGlobals;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Maps AppIds to their package names. */
+public final class AppIdToPackageMap {
+ private final Map<Integer, String> mAppIdToPackageMap;
+
+ @VisibleForTesting
+ public AppIdToPackageMap(Map<Integer, String> appIdToPackageMap) {
+ mAppIdToPackageMap = appIdToPackageMap;
+ }
+
+ /** Creates a new {@link AppIdToPackageMap} for currently installed packages. */
+ public static AppIdToPackageMap getSnapshot() {
+ List<PackageInfo> packages;
+ try {
+ packages = AppGlobals.getPackageManager()
+ .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE,
+ UserHandle.USER_SYSTEM).getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ final Map<Integer, String> map = new HashMap<>();
+ for (PackageInfo pkg : packages) {
+ final int uid = pkg.applicationInfo.uid;
+ if (pkg.sharedUserId != null && map.containsKey(uid)) {
+ // Use sharedUserId string as package name if there are collisions
+ map.put(uid, "shared:" + pkg.sharedUserId);
+ } else {
+ map.put(uid, pkg.packageName);
+ }
+ }
+ return new AppIdToPackageMap(map);
+ }
+
+ /** Maps the AppId to a package name. */
+ public String mapAppId(int appId) {
+ String pkgName = mAppIdToPackageMap.get(appId);
+ return pkgName == null ? String.valueOf(appId) : pkgName;
+ }
+
+ /** Maps the UID to a package name. */
+ public String mapUid(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ final String pkgName = mAppIdToPackageMap.get(appId);
+ final String uidStr = UserHandle.formatUid(uid);
+ return pkgName == null ? uidStr : pkgName + '/' + uidStr;
+ }
+}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 875d7c9..5465485 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -19,8 +19,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
+import android.os.Process;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.Pair;
@@ -60,7 +60,11 @@
private static final int CALL_SESSIONS_POOL_SIZE = 100;
private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
+ // Default values for overflow entry. The work source uid does not use a default value in order
+ // to have on overflow entry per work source uid.
private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
+ private static final boolean OVERFLOW_SCREEN_INTERACTIVE = false;
+ private static final int OVERFLOW_DIRECT_CALLING_UID = -1;
private static final int OVERFLOW_TRANSACTION_CODE = -1;
// Whether to collect all the data: cpu + exceptions + reply/request sizes.
@@ -78,8 +82,10 @@
private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
private long mCallStatsCount = 0;
+ private boolean mAddDebugEntries = false;
private CachedDeviceState.Readonly mDeviceState;
+ private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
/** Injector for {@link BinderCallsStats}. */
public static class Injector {
@@ -93,12 +99,16 @@
}
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.close();
+ }
mDeviceState = deviceState;
+ mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
}
@Override
@Nullable
- public CallSession callStarted(Binder binder, int code) {
+ public CallSession callStarted(Binder binder, int code, int workSourceUid) {
if (mDeviceState == null || mDeviceState.isCharging()) {
return null;
}
@@ -122,19 +132,21 @@
}
@Override
- public void callEnded(@Nullable CallSession s, int parcelRequestSize, int parcelReplySize) {
+ public void callEnded(@Nullable CallSession s, int parcelRequestSize,
+ int parcelReplySize, int workSourceUid) {
if (s == null) {
return;
}
- processCallEnded(s, parcelRequestSize, parcelReplySize);
+ processCallEnded(s, parcelRequestSize, parcelReplySize, workSourceUid);
if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
mCallSessionsPool.add(s);
}
}
- private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+ private void processCallEnded(CallSession s,
+ int parcelRequestSize, int parcelReplySize, int workSourceUid) {
// Non-negative time signals we need to record data for this call.
final boolean recordCall = s.cpuTimeStarted >= 0;
final long duration;
@@ -147,7 +159,6 @@
latencyDuration = 0;
}
final int callingUid = getCallingUid();
- final int workSourceUid = getWorkSourceUid();
synchronized (mLock) {
// This was already checked in #callStart but check again while synchronized.
@@ -155,8 +166,7 @@
return;
}
- final boolean isWorkSourceSet = workSourceUid >= 0;
- final UidEntry uidEntry = getUidEntry(isWorkSourceSet ? workSourceUid : callingUid);
+ final UidEntry uidEntry = getUidEntry(workSourceUid);
uidEntry.callCount++;
if (recordCall) {
@@ -317,9 +327,30 @@
exported.methodName = methodName;
}
+ // Debug entries added to help validate the data.
+ if (mAddDebugEntries && mBatteryStopwatch != null) {
+ resultCallStats.add(createDebugEntry("start_time_millis", mStartTime));
+ resultCallStats.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+ resultCallStats.add(
+ createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
+ }
+
return resultCallStats;
}
+ private ExportedCallStat createDebugEntry(String variableName, long value) {
+ final int uid = Process.myUid();
+ final ExportedCallStat callStat = new ExportedCallStat();
+ callStat.className = "";
+ callStat.workSourceUid = uid;
+ callStat.callingUid = uid;
+ callStat.recordedCallCount = 1;
+ callStat.callCount = 1;
+ callStat.methodName = "__DEBUG_" + variableName;
+ callStat.latencyMicros = value;
+ return callStat;
+ }
+
/** @hide */
public ArrayMap<String, Integer> getExportedExceptionStats() {
synchronized (mLock) {
@@ -328,19 +359,20 @@
}
/** Writes the collected statistics to the supplied {@link PrintWriter}.*/
- public void dump(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap, boolean verbose) {
+ public void dump(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
synchronized (mLock) {
- dumpLocked(pw, appIdToPkgNameMap, verbose);
+ dumpLocked(pw, packageMap, verbose);
}
}
- private void dumpLocked(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap,
- boolean verbose) {
+ private void dumpLocked(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
long totalCallsCount = 0;
long totalRecordedCallsCount = 0;
long totalCpuTime = 0;
pw.print("Start time: ");
pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+ pw.print("On battery time (ms): ");
+ pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
final List<UidEntry> entries = new ArrayList<>();
@@ -367,9 +399,9 @@
for (ExportedCallStat e : exportedCallStats) {
sb.setLength(0);
sb.append(" ")
- .append(uidToString(e.callingUid, appIdToPkgNameMap))
+ .append(packageMap.mapUid(e.callingUid))
.append(',')
- .append(uidToString(e.workSourceUid, appIdToPkgNameMap))
+ .append(packageMap.mapUid(e.workSourceUid))
.append(',').append(e.className)
.append('#').append(e.methodName)
.append(',').append(e.screenInteractive)
@@ -390,7 +422,7 @@
final List<UidEntry> summaryEntries = verbose ? entries
: getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
for (UidEntry entry : summaryEntries) {
- String uidStr = uidToString(entry.workSourceUid, appIdToPkgNameMap);
+ String uidStr = packageMap.mapUid(entry.workSourceUid);
pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
entry.recordedCallCount, entry.callCount, uidStr));
@@ -418,13 +450,6 @@
}
}
- private static String uidToString(int uid, Map<Integer, String> pkgNameMap) {
- final int appId = UserHandle.getAppId(uid);
- final String pkgName = pkgNameMap == null ? null : pkgNameMap.get(appId);
- final String uidStr = UserHandle.formatUid(uid);
- return pkgName == null ? uidStr : pkgName + '/' + uidStr;
- }
-
protected long getThreadTimeMicro() {
return SystemClock.currentThreadTimeMicro();
}
@@ -433,10 +458,6 @@
return Binder.getCallingUid();
}
- protected int getWorkSourceUid() {
- return Binder.getCallingWorkSourceUid();
- }
-
protected long getElapsedRealtimeMicro() {
return SystemClock.elapsedRealtimeNanos() / 1000;
}
@@ -457,6 +478,10 @@
}
}
+ public void setAddDebugEntries(boolean addDebugEntries) {
+ mAddDebugEntries = addDebugEntries;
+ }
+
/**
* Sets the maximum number of items to track.
*/
@@ -496,6 +521,9 @@
mUidEntries.clear();
mExceptionCounts.clear();
mStartTime = System.currentTimeMillis();
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.reset();
+ }
}
}
@@ -632,14 +660,16 @@
// Only create CallStat if it's a new entry, otherwise update existing instance.
if (mapCallStat == null) {
if (maxCallStatsReached) {
- mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE,
- screenInteractive);
+ mapCallStat = get(OVERFLOW_DIRECT_CALLING_UID, OVERFLOW_BINDER,
+ OVERFLOW_TRANSACTION_CODE, OVERFLOW_SCREEN_INTERACTIVE);
if (mapCallStat != null) {
return mapCallStat;
}
+ callingUid = OVERFLOW_DIRECT_CALLING_UID;
binderClass = OVERFLOW_BINDER;
transactionCode = OVERFLOW_TRANSACTION_CODE;
+ screenInteractive = OVERFLOW_SCREEN_INTERACTIVE;
}
mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 0155067..5b69979 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -22,7 +22,6 @@
import android.os.IBinder;
import android.os.SystemClock;
import android.util.EventLog;
-import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.util.Preconditions;
@@ -86,6 +85,22 @@
boolean exceptionThrown;
}
+
+ /**
+ * Responsible for resolving a work source.
+ */
+ @FunctionalInterface
+ public interface WorkSourceProvider {
+ /**
+ * <p>This method is called in a critical path of the binder transaction.
+ * <p>The implementation should never execute a binder call since it is called during a
+ * binder transaction.
+ *
+ * @return the uid of the process to attribute the binder transaction to.
+ */
+ int resolveWorkSourceUid();
+ }
+
/**
* Allows to track various steps of an API call.
*/
@@ -95,14 +110,16 @@
*
* @return a CallSession to pass to the callEnded method.
*/
- CallSession callStarted(Binder binder, int code);
+ CallSession callStarted(Binder binder, int code, int workSourceUid);
/**
* Called when a binder call stops.
*
- * <li>This method will be called even when an exception is thrown.
+ * <li>This method will be called even when an exception is thrown by the binder stub
+ * implementation.
*/
- void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize);
+ void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize,
+ int workSourceUid);
/**
* Called if an exception is thrown while executing the binder transaction.
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
index 8c90682..334cca3 100644
--- a/core/java/com/android/internal/os/CachedDeviceState.java
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -16,8 +16,14 @@
package com.android.internal.os;
+
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayList;
+
/**
* Stores the device state (e.g. charging/on battery, screen on/off) to be shared with
* the System Server telemetry services.
@@ -27,6 +33,9 @@
public class CachedDeviceState {
private volatile boolean mScreenInteractive;
private volatile boolean mCharging;
+ private final Object mStopwatchesLock = new Object();
+ @GuardedBy("mStopwatchLock")
+ private final ArrayList<TimeInStateStopwatch> mOnBatteryStopwatches = new ArrayList<>();
public CachedDeviceState() {
mCharging = true;
@@ -44,7 +53,23 @@
}
public void setCharging(boolean charging) {
- mCharging = charging;
+ if (mCharging != charging) {
+ mCharging = charging;
+ updateStopwatches(/* shouldStart= */ !charging);
+ }
+ }
+
+ private void updateStopwatches(boolean shouldStart) {
+ synchronized (mStopwatchesLock) {
+ final int size = mOnBatteryStopwatches.size();
+ for (int i = 0; i < size; i++) {
+ if (shouldStart) {
+ mOnBatteryStopwatches.get(i).start();
+ } else {
+ mOnBatteryStopwatches.get(i).stop();
+ }
+ }
+ }
}
public Readonly getReadonlyClient() {
@@ -62,5 +87,74 @@
public boolean isScreenInteractive() {
return mScreenInteractive;
}
+
+ /** Creates a {@link TimeInStateStopwatch stopwatch} that tracks the time on battery. */
+ public TimeInStateStopwatch createTimeOnBatteryStopwatch() {
+ synchronized (mStopwatchesLock) {
+ final TimeInStateStopwatch stopwatch = new TimeInStateStopwatch();
+ mOnBatteryStopwatches.add(stopwatch);
+ if (!mCharging) {
+ stopwatch.start();
+ }
+ return stopwatch;
+ }
+ }
+ }
+
+ /** Tracks the time the device spent in a given state. */
+ public class TimeInStateStopwatch implements AutoCloseable {
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private long mStartTimeMillis;
+ @GuardedBy("mLock")
+ private long mTotalTimeMillis;
+
+ /** Returns the time in state since the last call to {@link TimeInStateStopwatch#reset}. */
+ public long getMillis() {
+ synchronized (mLock) {
+ return mTotalTimeMillis + elapsedTime();
+ }
+ }
+
+ /** Resets the time in state to 0 without stopping the timer if it's started. */
+ public void reset() {
+ synchronized (mLock) {
+ mTotalTimeMillis = 0;
+ mStartTimeMillis = isRunning() ? SystemClock.elapsedRealtime() : 0;
+ }
+ }
+
+ private void start() {
+ synchronized (mLock) {
+ if (!isRunning()) {
+ mStartTimeMillis = SystemClock.elapsedRealtime();
+ }
+ }
+ }
+
+ private void stop() {
+ synchronized (mLock) {
+ if (isRunning()) {
+ mTotalTimeMillis += elapsedTime();
+ mStartTimeMillis = 0;
+ }
+ }
+ }
+
+ private long elapsedTime() {
+ return isRunning() ? SystemClock.elapsedRealtime() - mStartTimeMillis : 0;
+ }
+
+ @VisibleForTesting
+ public boolean isRunning() {
+ return mStartTimeMillis > 0;
+ }
+
+ @Override
+ public void close() {
+ synchronized (mStopwatchesLock) {
+ mOnBatteryStopwatches.remove(this);
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index cf2a297..01fd8ba 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -49,6 +49,7 @@
private final int mEntriesSizeCap;
private int mSamplingInterval;
private CachedDeviceState.Readonly mDeviceState;
+ private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
private long mStartTime = System.currentTimeMillis();
private boolean mAddDebugEntries = false;
@@ -58,7 +59,12 @@
}
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.close();
+ }
+
mDeviceState = deviceState;
+ mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
}
public void setAddDebugEntries(boolean addDebugEntries) {
@@ -148,9 +154,11 @@
maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
// Debug entries added to help validate the data.
- if (mAddDebugEntries) {
+ if (mAddDebugEntries && mBatteryStopwatch != null) {
exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+ exportedEntries.add(
+ createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
}
return exportedEntries;
}
@@ -159,7 +167,7 @@
final Entry entry = new Entry("__DEBUG_" + variableName);
entry.messageCount = 1;
entry.recordedMessageCount = 1;
- entry.maxDelayMillis = value;
+ entry.totalLatencyMicro = value;
return new ExportedEntry(entry);
}
@@ -168,6 +176,10 @@
return mStartTime;
}
+ public long getBatteryTimeMillis() {
+ return mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0;
+ }
+
private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) {
synchronized (specialEntry) {
if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) {
@@ -188,6 +200,9 @@
mOverflowEntry.reset();
}
mStartTime = System.currentTimeMillis();
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.reset();
+ }
}
public void setSamplingInterval(int samplingInterval) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 600b1b3..53b56f2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -29,7 +29,7 @@
{
void setIcon(String slot, in StatusBarIcon icon);
void removeIcon(String slot);
- void disable(int state1, int state2);
+ void disable(int displayId, int state1, int state2);
void animateExpandNotificationsPanel();
void animateExpandSettingsPanel(String subPanel);
void animateCollapsePanels();
@@ -38,8 +38,9 @@
void showWirelessChargingAnimation(int batteryLevel);
/**
- * Notifies the status bar of a System UI visibility flag change.
+ * Notifies System UI side of a visibility flag change on the specified display.
*
+ * @param displayId the id of the display to notify
* @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
* separately in fullscreenStackVis and dockedStackVis
* @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
@@ -50,13 +51,13 @@
* @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
* @param dockedBounds the current bounds of the docked stack, in screen coordinates
*/
- void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
- in Rect fullscreenBounds, in Rect dockedBounds);
+ void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, in Rect fullscreenBounds, in Rect dockedBounds);
- void topAppWindowChanged(boolean menuVisible);
- void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+ void topAppWindowChanged(int displayId, boolean menuVisible);
+ void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
- void setWindowState(int window, int state);
+ void setWindowState(int display, int window, int state);
void showRecentApps(boolean triggeredFromAltTab);
void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
@@ -70,30 +71,38 @@
void toggleKeyboardShortcutsMenu(int deviceId);
/**
- * Notifies the status bar that an app transition is pending to delay applying some flags with
- * visual impact until {@link #appTransitionReady} is called.
- */
- void appTransitionPending();
-
- /**
- * Notifies the status bar that a pending app transition has been cancelled.
- */
- void appTransitionCancelled();
-
- /**
- * Notifies the status bar that an app transition is now being executed.
+ * Notifies System UI on the specified display that an app transition is pending to delay
+ * applying some flags with visual impact until {@link #appTransitionReady} is called.
*
+ * @param displayId the id of the display to notify
+ */
+ void appTransitionPending(int displayId);
+
+ /**
+ * Notifies System UI on the specified display that a pending app transition has been cancelled.
+ *
+ * @param displayId the id of the display to notify
+ */
+ void appTransitionCancelled(int displayId);
+
+ /**
+ * Notifies System UI on the specified display that an app transition is now being executed.
+ *
+ * @param displayId the id of the display to notify
* @param statusBarAnimationsStartTime the desired start time for all visual animations in the
* status bar caused by this app transition in uptime millis
* @param statusBarAnimationsDuration the duration for all visual animations in the status
* bar caused by this app transition in millis
*/
- void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
+ void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
+ long statusBarAnimationsDuration);
/**
- * Notifies the status bar that an app transition is done.
+ * Notifies System UI on the specified display that an app transition is done.
+ *
+ * @param displayId the id of the display to notify
*/
- void appTransitionFinished();
+ void appTransitionFinished(int displayId);
void showAssistDisclosure();
void startAssist(in Bundle args);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bf82dc61..5118e5f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -41,6 +41,7 @@
void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
void setIconVisibility(String slot, boolean visible);
void removeIcon(String slot);
+ // TODO(b/117478341): support back button change when IME is showing on a external display.
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
@@ -69,7 +70,7 @@
void onNotificationSmartRepliesAdded(in String key, in int replyCount);
void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
void onNotificationSettingsViewed(String key);
- void setSystemUiVisibility(int vis, int mask, String cause);
+ void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
void onGlobalActionsShown();
void onGlobalActionsHidden();
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 4b66267..f669e94 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -32,6 +32,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.IntFunction;
/**
* ArrayUtils contains some methods that you can call to find out
@@ -656,4 +657,30 @@
throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
}
}
+
+ /**
+ * Returns an array with values from {@code val} minus {@code null} values
+ *
+ * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new}
+ */
+ public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) {
+ int nullCount = 0;
+ int size = size(val);
+ for (int i = 0; i < size; i++) {
+ if (val[i] == null) {
+ nullCount++;
+ }
+ }
+ if (nullCount == 0) {
+ return val;
+ }
+ T[] result = arrayConstructor.apply(size - nullCount);
+ int outIdx = 0;
+ for (int i = 0; i < size; i++) {
+ if (val[i] != null) {
+ result[outIdx++] = val[i];
+ }
+ }
+ return result;
+ }
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1e71bd1..f62c440 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -64,6 +64,8 @@
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
+ void showInputMethodPickerFromSystem(in IInputMethodClient client, int auxiliarySubtypeMode,
+ int displayId);
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
boolean isInputMethodPickerShownForTest();
// TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
@@ -74,10 +76,6 @@
boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
InputMethodSubtype getCurrentInputMethodSubtype();
boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
- // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
- boolean switchToPreviousInputMethod(in IBinder token);
- // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
- boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
// This is kept due to @UnsupportedAppUsage.
// TODO(Bug 113914148): Consider removing this.
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/com/android/internal/widget/ImageMessageConsumer.java
similarity index 60%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to core/java/com/android/internal/widget/ImageMessageConsumer.java
index c66a6cb..01613dc 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/java/com/android/internal/widget/ImageMessageConsumer.java
@@ -14,6 +14,15 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package com.android.internal.widget;
-parcelable ContentCaptureEvent;
+/**
+ * An interface for the class who will use the {@link ImageResolver} to resolve images.
+ */
+public interface ImageMessageConsumer {
+ /**
+ * Set the custom {@link ImageResolver} other than {@link LocalImageResolver}.
+ * @param resolver An image resolver that has custom implementation.
+ */
+ void setImageResolver(ImageResolver resolver);
+}
diff --git a/core/java/com/android/internal/widget/ImageResolver.java b/core/java/com/android/internal/widget/ImageResolver.java
new file mode 100644
index 0000000..4588525
--- /dev/null
+++ b/core/java/com/android/internal/widget/ImageResolver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+/**
+ * An interface for image resolvers that have custom implementations like cache mechanisms.
+ */
+public interface ImageResolver {
+ /**
+ * Load an image from specified uri.
+ * @param uri Uri of the target image.
+ * @return Target image in Drawable.
+ */
+ Drawable loadImage(Uri uri);
+}
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index 71d3bb5..2302de2 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -17,7 +17,6 @@
package com.android.internal.widget;
import android.annotation.Nullable;
-import android.app.Notification;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index c96bacd..d5dc703 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -840,6 +840,11 @@
+ "of length " + MIN_LOCK_PASSWORD_SIZE);
}
+ if (requestedQuality < PASSWORD_QUALITY_NUMERIC) {
+ throw new IllegalArgumentException("quality must be at least NUMERIC, but was "
+ + requestedQuality);
+ }
+
final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
setKeyguardStoredPasswordQuality(
computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality),
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index 607a3a9..64650a7 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -25,6 +25,7 @@
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pools;
@@ -57,6 +58,7 @@
private int mActualWidth;
private int mActualHeight;
private boolean mIsIsolated;
+ private ImageResolver mImageResolver;
public MessagingImageMessage(@NonNull Context context) {
this(context, null);
@@ -96,11 +98,16 @@
MessagingMessage.super.setMessage(message);
Drawable drawable;
try {
- drawable = LocalImageResolver.resolveImage(message.getDataUri(), getContext());
+ Uri uri = message.getDataUri();
+ drawable = mImageResolver != null ? mImageResolver.loadImage(uri) :
+ LocalImageResolver.resolveImage(uri, getContext());
} catch (IOException | SecurityException e) {
e.printStackTrace();
return false;
}
+ if (drawable == null) {
+ return false;
+ }
int intrinsicHeight = drawable.getIntrinsicHeight();
if (intrinsicHeight == 0) {
Log.w(TAG, "Drawable with 0 intrinsic height was returned");
@@ -114,7 +121,7 @@
}
static MessagingMessage createMessage(MessagingLayout layout,
- Notification.MessagingStyle.Message m) {
+ Notification.MessagingStyle.Message m, ImageResolver resolver) {
MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
MessagingImageMessage createdMessage = sInstancePool.acquire();
if (createdMessage == null) {
@@ -125,6 +132,7 @@
false);
createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
}
+ createdMessage.setImageResolver(resolver);
boolean created = createdMessage.setMessage(m);
if (!created) {
createdMessage.recycle();
@@ -133,6 +141,10 @@
return createdMessage;
}
+ private void setImageResolver(ImageResolver resolver) {
+ mImageResolver = resolver;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 0f2e9c5..07d0d7d 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -57,7 +57,7 @@
* messages and adapts the layout accordingly.
*/
@RemoteViews.RemoteView
-public class MessagingLayout extends FrameLayout {
+public class MessagingLayout extends FrameLayout implements ImageMessageConsumer {
private static final float COLOR_SHIFT_AMOUNT = 60;
/**
@@ -95,6 +95,7 @@
private Person mUser;
private CharSequence mNameReplacement;
private boolean mDisplayImagesAtEnd;
+ private ImageResolver mImageResolver;
public MessagingLayout(@NonNull Context context) {
super(context);
@@ -167,6 +168,11 @@
bind(newMessages, newHistoricMessages, showSpinner);
}
+ @Override
+ public void setImageResolver(ImageResolver resolver) {
+ mImageResolver = resolver;
+ }
+
private void addRemoteInputHistoryToMessages(
List<Notification.MessagingStyle.Message> newMessages,
CharSequence[] remoteInputHistory) {
@@ -463,12 +469,12 @@
*/
private List<MessagingMessage> createMessages(
List<Notification.MessagingStyle.Message> newMessages, boolean historic) {
- List<MessagingMessage> result = new ArrayList<>();;
+ List<MessagingMessage> result = new ArrayList<>();
for (int i = 0; i < newMessages.size(); i++) {
Notification.MessagingStyle.Message m = newMessages.get(i);
MessagingMessage message = findAndRemoveMatchingMessage(m);
if (message == null) {
- message = MessagingMessage.createMessage(this, m);
+ message = MessagingMessage.createMessage(this, m, mImageResolver);
}
message.setIsHistoric(historic);
result.add(message);
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index 74d0aae..c32d370 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -33,9 +33,9 @@
String IMAGE_MIME_TYPE_PREFIX = "image/";
static MessagingMessage createMessage(MessagingLayout layout,
- Notification.MessagingStyle.Message m) {
+ Notification.MessagingStyle.Message m, ImageResolver resolver) {
if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) {
- return MessagingImageMessage.createMessage(layout, m);
+ return MessagingImageMessage.createMessage(layout, m, resolver);
} else {
return MessagingTextMessage.createMessage(layout, m);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index b97a9fa..2e674a5 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -69,6 +69,9 @@
private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x40;
private static final int ALLOW_ALL = ~0;
+ // property for runtime configuration differentiation
+ private static final String SKU_PROPERTY = "ro.boot.product.hardware.sku";
+
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
int[] mGlobalGids;
@@ -78,10 +81,23 @@
final ArrayList<SplitPermissionInfo> mSplitPermissions = new ArrayList<>();
+ public static final class SharedLibraryEntry {
+ public final String name;
+ public final String filename;
+ public final String[] dependencies;
+
+ SharedLibraryEntry(String name, String filename, String[] dependencies) {
+ this.name = name;
+ this.filename = filename;
+ this.dependencies = dependencies;
+ }
+ }
+
// These are the built-in shared libraries that were read from the
- // system configuration files. Keys are the library names; strings are the
- // paths to the libraries.
- final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>();
+ // system configuration files. Keys are the library names; values are
+ // the individual entries that contain information such as filename
+ // and dependencies.
+ final ArrayMap<String, SharedLibraryEntry> mSharedLibraries = new ArrayMap<>();
// These are the features this devices supports that were read from the
// system configuration files.
@@ -200,7 +216,7 @@
return mSplitPermissions;
}
- public ArrayMap<String, String> getSharedLibraries() {
+ public ArrayMap<String, SharedLibraryEntry> getSharedLibraries() {
return mSharedLibraries;
}
@@ -332,6 +348,17 @@
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
+ String skuProperty = SystemProperties.get(SKU_PROPERTY, "");
+ if (!skuProperty.isEmpty()) {
+ String skuDir = "sku_" + skuProperty;
+
+ readPermissions(Environment.buildPath(
+ Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag);
+ readPermissions(Environment.buildPath(
+ Environment.getOdmDirectory(), "etc", "permissions", skuDir),
+ odmPermissionFlag);
+ }
+
// Allow OEM to customize features and OEM permissions
int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
readPermissions(Environment.buildPath(
@@ -368,6 +395,10 @@
// Iterate over the files in the directory and scan .xml files
File platformFile = null;
for (File f : libraryDir.listFiles()) {
+ if (!f.isFile()) {
+ continue;
+ }
+
// We'll read platform.xml last
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
@@ -497,6 +528,7 @@
} else if ("library".equals(name) && allowLibs) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
+ String ldependency = parser.getAttributeValue(null, "dependency");
if (lname == null) {
Slog.w(TAG, "<library> without name in " + permFile + " at "
+ parser.getPositionDescription());
@@ -505,11 +537,12 @@
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
- mSharedLibraries.put(lname, lfile);
+ SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
+ ldependency == null ? new String[0] : ldependency.split(":"));
+ mSharedLibraries.put(lname, entry);
}
XmlUtils.skipCurrentTag(parser);
continue;
-
} else if ("feature".equals(name) && allowFeatures) {
String fname = parser.getAttributeValue(null, "name");
int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 31bb1d5..43f8d00 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -132,7 +132,6 @@
"android/graphics/GIFMovie.cpp",
"android/graphics/GraphicBuffer.cpp",
"android/graphics/Graphics.cpp",
- "android/graphics/HarfBuzzNGFaceSkia.cpp",
"android/graphics/ImageDecoder.cpp",
"android/graphics/Interpolator.cpp",
"android/graphics/MaskFilter.cpp",
@@ -162,6 +161,7 @@
"android/graphics/pdf/PdfUtils.cpp",
"android/graphics/text/LineBreaker.cpp",
"android/graphics/text/MeasuredText.cpp",
+ "android_media_AudioEffectDescriptor.cpp",
"android_media_AudioRecord.cpp",
"android_media_AudioSystem.cpp",
"android_media_AudioTrack.cpp",
@@ -264,6 +264,7 @@
"libEGL",
"libGLESv1_CM",
"libGLESv2",
+ "libGLESv3",
"libvulkan",
"libziparchive",
"libETC1",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index bc1d5cc..687b105 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -103,6 +103,7 @@
extern int register_android_hardware_UsbRequest(JNIEnv *env);
extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env);
+extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
extern int register_android_media_AudioTrack(JNIEnv *env);
@@ -1056,12 +1057,18 @@
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
- LOG_FATAL("No root directory specified, and /android does not exist.");
+ LOG_FATAL("No root directory specified, and /system does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
+ const char* runtimeRootDir = getenv("ANDROID_RUNTIME_ROOT");
+ if (runtimeRootDir == NULL) {
+ LOG_FATAL("No runtime directory specified with ANDROID_RUNTIME_ROOT environment variable.");
+ return;
+ }
+
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
@@ -1450,6 +1457,7 @@
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
+ REG_JNI(register_android_media_AudioEffectDescriptor),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioTrack),
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
deleted file mode 100644
index dcb7874..0000000
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (c) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define LOG_TAG "TextLayoutCache"
-
-#include "HarfBuzzNGFaceSkia.h"
-
-#include <stdlib.h>
-
-#include <log/log.h>
-
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkPoint.h>
-#include <SkRect.h>
-#include <SkTypeface.h>
-
-#include <hb.h>
-
-namespace android {
-
-static const bool kDebugGlyphs = false;
-
-// Our implementation of the callbacks which Harfbuzz requires by using Skia
-// calls. See the Harfbuzz source for references about what these callbacks do.
-
-struct HarfBuzzFontData {
- explicit HarfBuzzFontData(SkPaint* paint) : m_paint(paint) { }
- SkPaint* m_paint;
-};
-
-static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
-{
- ALOG_ASSERT(codepoint <= 0xFFFF);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- SkScalar skWidth;
- SkRect skBounds;
- uint16_t glyph = codepoint;
-
- paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
- if (kDebugGlyphs) {
- ALOGD("returned glyph for %i: width = %f", codepoint, skWidth);
- }
- if (width)
- *width = SkScalarToHBFixed(skWidth);
- if (extents) {
- // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up.
- extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft);
- extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop);
- extents->width = SkScalarToHBFixed(skBounds.width());
- extents->height = SkScalarToHBFixed(-skBounds.height());
- }
-}
-
-static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
- SkPaint* paint = hbFontData->m_paint;
- paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
-
- if (unicode > 0x10ffff) {
- unicode = 0xfffd;
- }
- SkUnichar unichar = unicode;
-
- uint16_t glyph16;
- paint->textToGlyphs(&unichar, sizeof(unichar), &glyph16);
- *glyph = glyph16;
- return !!*glyph;
-}
-
-static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
- hb_position_t advance = 0;
-
- SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, &advance, 0);
- return advance;
-}
-
-static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
-{
- // Just return true, following the way that Harfbuzz-FreeType
- // implementation does.
- return true;
-}
-
-static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
-
- SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, 0, extents);
- return true;
-}
-
-static hb_font_funcs_t* harfbuzzSkiaGetFontFuncs()
-{
- static hb_font_funcs_t* harfbuzzSkiaFontFuncs = 0;
-
- // We don't set callback functions which we can't support.
- // Harfbuzz will use the fallback implementation if they aren't set.
- if (!harfbuzzSkiaFontFuncs) {
- harfbuzzSkiaFontFuncs = hb_font_funcs_create();
- hb_font_funcs_set_glyph_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyph, 0, 0);
- hb_font_funcs_set_glyph_h_advance_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
- hb_font_funcs_set_glyph_h_origin_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
- hb_font_funcs_set_glyph_extents_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphExtents, 0, 0);
- hb_font_funcs_make_immutable(harfbuzzSkiaFontFuncs);
- }
- return harfbuzzSkiaFontFuncs;
-}
-
-hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData)
-{
- SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData);
-
- const size_t tableSize = typeface->getTableSize(tag);
- if (!tableSize)
- return 0;
-
- char* buffer = reinterpret_cast<char*>(malloc(tableSize));
- if (!buffer)
- return 0;
- size_t actualSize = typeface->getTableData(tag, 0, tableSize, buffer);
- if (tableSize != actualSize) {
- free(buffer);
- return 0;
- }
-
- return hb_blob_create(const_cast<char*>(buffer), tableSize,
- HB_MEMORY_MODE_WRITABLE, buffer, free);
-}
-
-static void destroyHarfBuzzFontData(void* data) {
- delete (HarfBuzzFontData*)data;
-}
-
-hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) {
- hb_font_t* font = hb_font_create(face);
-
- // Note: this needs to be reworked when we do subpixels
- int x_ppem = floor(sizeX + 0.5);
- int y_ppem = floor(sizeY + 0.5);
- hb_font_set_ppem(font, x_ppem, y_ppem);
- hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY));
-
- HarfBuzzFontData* data = new HarfBuzzFontData(paint);
- hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData);
-
- return font;
-}
-
-} // namespace android
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index a8b0640..c249e20 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -71,7 +71,7 @@
static void defaultSettingsForAndroid(Paint* paint) {
// GlyphID encoding is required because we are using Harfbuzz shaping
- paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+ paint->setTextEncoding(kGlyphID_SkTextEncoding);
}
namespace PaintGlue {
@@ -321,7 +321,7 @@
x += MinikinUtils::xOffsetForTextAlign(paint, layout);
Paint::Align align = paint->getTextAlign();
paint->setTextAlign(Paint::kLeft_Align);
- paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+ paint->setTextEncoding(kGlyphID_SkTextEncoding);
GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
MinikinUtils::forFontRun(layout, paint, f);
paint->setTextAlign(align);
diff --git a/core/jni/android/graphics/text/MeasuredText.cpp b/core/jni/android/graphics/text/MeasuredText.cpp
index 0bfadb4..d7d96fb 100644
--- a/core/jni/android/graphics/text/MeasuredText.cpp
+++ b/core/jni/android/graphics/text/MeasuredText.cpp
@@ -84,14 +84,14 @@
// Regular JNI
static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
- jcharArray javaText, jboolean computeHyphenation,
- jboolean computeLayout) {
+ jlong hintPtr, jcharArray javaText, jboolean computeHyphenation,
+ jboolean computeLayout) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
// Pass the ownership to Java.
- return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation,
- computeLayout).release());
+ return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation, computeLayout,
+ toMeasuredParagraph(hintPtr)).release());
}
// Regular JNI
@@ -147,7 +147,7 @@
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildMeasuredText", "(J[CZZ)J", (void*) nBuildMeasuredText},
+ {"nBuildMeasuredText", "(JJ[CZZ)J", (void*) nBuildMeasuredText},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
};
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 98bc735..8a28034 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -225,7 +225,8 @@
gAudioFormatCstor,
audioFormatFromNative(event->audio_config.format),
event->audio_config.sample_rate,
- inChannelMaskFromNative(event->audio_config.channel_mask));
+ inChannelMaskFromNative(event->audio_config.channel_mask),
+ (jint)0 /* channelIndexMask */);
}
if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) {
diff --git a/core/jni/android_media_AudioEffectDescriptor.cpp b/core/jni/android_media_AudioEffectDescriptor.cpp
new file mode 100644
index 0000000..5175a05
--- /dev/null
+++ b/core/jni/android_media_AudioEffectDescriptor.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core_jni_helpers.h"
+#include "android_media_AudioErrors.h"
+#include "media/AudioEffect.h"
+
+using namespace android;
+
+static jclass gAudioEffectDescriptorClass;
+static jmethodID gAudioEffectDescriptorCstor;
+
+namespace android {
+
+jclass audioEffectDescriptorClass() {
+ return gAudioEffectDescriptorClass;
+}
+
+jint convertAudioEffectDescriptorFromNative(JNIEnv* env, jobject* jDescriptor,
+ const effect_descriptor_t* nDescriptor)
+{
+ jstring jType;
+ jstring jUuid;
+ jstring jConnect;
+ jstring jName;
+ jstring jImplementor;
+ char str[EFFECT_STRING_LEN_MAX];
+
+ if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
+ == EFFECT_FLAG_TYPE_AUXILIARY) {
+ jConnect = env->NewStringUTF("Auxiliary");
+ } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
+ == EFFECT_FLAG_TYPE_INSERT) {
+ jConnect = env->NewStringUTF("Insert");
+ } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
+ == EFFECT_FLAG_TYPE_PRE_PROC) {
+ jConnect = env->NewStringUTF("Pre Processing");
+ } else {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+
+ AudioEffect::guidToString(&nDescriptor->type, str, EFFECT_STRING_LEN_MAX);
+ jType = env->NewStringUTF(str);
+
+ AudioEffect::guidToString(&nDescriptor->uuid, str, EFFECT_STRING_LEN_MAX);
+ jUuid = env->NewStringUTF(str);
+
+ jName = env->NewStringUTF(nDescriptor->name);
+ jImplementor = env->NewStringUTF(nDescriptor->implementor);
+
+ *jDescriptor = env->NewObject(gAudioEffectDescriptorClass,
+ gAudioEffectDescriptorCstor,
+ jType,
+ jUuid,
+ jConnect,
+ jName,
+ jImplementor);
+ env->DeleteLocalRef(jType);
+ env->DeleteLocalRef(jUuid);
+ env->DeleteLocalRef(jConnect);
+ env->DeleteLocalRef(jName);
+ env->DeleteLocalRef(jImplementor);
+
+ return (jint) AUDIO_JAVA_SUCCESS;
+}
+
+void convertAudioEffectDescriptorVectorFromNative(JNIEnv *env, jobjectArray *jDescriptors,
+ const std::vector<effect_descriptor_t>& nDescriptors)
+{
+ jobjectArray temp = env->NewObjectArray(nDescriptors.size(),
+ audioEffectDescriptorClass(), NULL);
+ size_t actualSize = 0;
+ for (size_t i = 0; i < nDescriptors.size(); i++) {
+ jobject jdesc;
+ if (convertAudioEffectDescriptorFromNative(env,
+ &jdesc,
+ &nDescriptors[i])
+ != AUDIO_JAVA_SUCCESS) {
+ continue;
+ }
+
+ env->SetObjectArrayElement(temp, actualSize++, jdesc);
+ env->DeleteLocalRef(jdesc);
+ }
+
+ *jDescriptors = env->NewObjectArray(actualSize, audioEffectDescriptorClass(), NULL);
+ for (size_t i = 0; i < actualSize; i++) {
+ env->SetObjectArrayElement(*jDescriptors,
+ i,
+ env->GetObjectArrayElement(temp, i));
+ }
+ env->DeleteLocalRef(temp);
+}
+
+}; // namespace android
+
+int register_android_media_AudioEffectDescriptor(JNIEnv* env) {
+ jclass audioEffectDescriptorClass =
+ FindClassOrDie(env, "android/media/audiofx/AudioEffect$Descriptor");
+ gAudioEffectDescriptorClass =
+ MakeGlobalRefOrDie(env, audioEffectDescriptorClass);
+ gAudioEffectDescriptorCstor =
+ GetMethodIDOrDie(env,
+ audioEffectDescriptorClass,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+
+ env->DeleteLocalRef(audioEffectDescriptorClass);
+ return 0;
+}
diff --git a/core/jni/android_media_AudioEffectDescriptor.h b/core/jni/android_media_AudioEffectDescriptor.h
new file mode 100644
index 0000000..d07188c
--- /dev/null
+++ b/core/jni/android_media_AudioEffectDescriptor.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_AUDIOEFFECT_DESCRIPTOR_H
+#define ANDROID_MEDIA_AUDIOEFFECT_DESCRIPTOR_H
+
+#include <system/audio.h>
+#include <system/audio_effect.h>
+
+#include "jni.h"
+
+namespace android {
+
+// Conversion from C effect_descriptor_t to Java AudioEffect.Descriptor object
+
+extern jclass audioEffectDescriptorClass();
+
+extern jint convertAudioEffectDescriptorFromNative(JNIEnv *env, jobject *jDescriptor,
+ const effect_descriptor_t *nDescriptor);
+
+extern void convertAudioEffectDescriptorVectorFromNative(JNIEnv *env, jobjectArray *jDescriptors,
+ const std::vector<effect_descriptor_t>& nDescriptors);
+} // namespace android
+
+#endif
\ No newline at end of file
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 1ea4ed1..12a8343b4 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -842,6 +842,18 @@
}
// ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) {
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioRecord pointer for getId()");
+ return (jint)AUDIO_PORT_HANDLE_NONE;
+ }
+ return (jint)lpRecorder->getPortId();
+}
+
+
+// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
@@ -883,6 +895,7 @@
(void *)android_media_AudioRecord_get_timestamp},
{"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
(void *)android_media_AudioRecord_get_active_microphones},
+ {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
};
// field names found in android/media/AudioRecord.java
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index adab8e2..283eb03 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2061,6 +2061,12 @@
return (jint)nativeToJavaStatus(status);
}
+static jboolean
+android_media_AudioSystem_isHapticPlaybackSupported(JNIEnv *env, jobject thiz)
+{
+ return AudioSystem::isHapticPlaybackSupported();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -2123,6 +2129,7 @@
{"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
{"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
{"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
+ {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported},
};
static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 04f0a53..516093e 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -23,8 +23,6 @@
#include <nativehelper/JniConstants.h>
#include "core_jni_helpers.h"
-#include <nativehelper/ScopedBytes.h>
-
#include <utils/Log.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
@@ -481,6 +479,24 @@
}
// ----------------------------------------------------------------------------
+static jboolean
+android_media_AudioTrack_is_direct_output_supported(JNIEnv *env, jobject thiz,
+ jint encoding, jint sampleRate,
+ jint channelMask, jint channelIndexMask,
+ jint contentType, jint usage, jint flags) {
+ audio_config_base_t config = {};
+ audio_attributes_t attributes = {};
+ config.format = static_cast<audio_format_t>(audioFormatToNative(encoding));
+ config.sample_rate = static_cast<uint32_t>(sampleRate);
+ config.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
+ attributes.content_type = static_cast<audio_content_type_t>(contentType);
+ attributes.usage = static_cast<audio_usage_t>(usage);
+ attributes.flags = static_cast<audio_flags_mask_t>(flags);
+ // ignore source and tags attributes as they don't affect querying whether output is supported
+ return AudioTrack::isDirectOutputSupported(config, attributes);
+}
+
+// ----------------------------------------------------------------------------
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
@@ -712,7 +728,7 @@
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
- jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
+ jobject javaByteBuffer, jint byteOffset, jint sizeInBytes,
jint javaAudioFormat, jboolean isWriteBlocking) {
//ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
// offsetInBytes, sizeInBytes);
@@ -723,13 +739,14 @@
return (jint)AUDIO_JAVA_INVALID_OPERATION;
}
- ScopedBytesRO bytes(env, javaBytes);
- if (bytes.get() == NULL) {
+ const jbyte* bytes =
+ reinterpret_cast<const jbyte*>(env->GetDirectBufferAddress(javaByteBuffer));
+ if (bytes == NULL) {
ALOGE("Error retrieving source of audio data to play, can't play");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
- jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
+ jint written = writeToTrack(lpTrack, javaAudioFormat, bytes, byteOffset,
sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
return written;
@@ -1284,9 +1301,23 @@
}
// ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_get_port_id(JNIEnv *env, jobject thiz) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return (jint)AUDIO_PORT_HANDLE_NONE;
+ }
+ return (jint)lpTrack->getPortId();
+}
+
+// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
+ {"native_is_direct_output_supported",
+ "(IIIIIII)Z",
+ (void *)android_media_AudioTrack_is_direct_output_supported},
{"native_start", "()V", (void *)android_media_AudioTrack_start},
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
@@ -1297,7 +1328,7 @@
{"native_release", "()V", (void *)android_media_AudioTrack_release},
{"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
{"native_write_native_bytes",
- "(Ljava/lang/Object;IIIZ)I",
+ "(Ljava/nio/ByteBuffer;IIIZ)I",
(void *)android_media_AudioTrack_write_native_bytes},
{"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
{"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
@@ -1354,6 +1385,7 @@
"(I)Landroid/media/VolumeShaper$State;",
(void *)android_media_AudioTrack_get_volume_shaper_state},
{"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
+ {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
};
@@ -1379,7 +1411,6 @@
}
}
-
// ----------------------------------------------------------------------------
int register_android_media_AudioTrack(JNIEnv *env)
{
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b1a9866..06625b3 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -32,15 +32,16 @@
android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
}
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jboolean devOptIn,
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring devOptIn,
jobject rulesFd, jlong rulesOffset, jlong rulesLength) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars appNameChars(env, appName);
+ ScopedUtfChars devOptInChars(env, devOptIn);
int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
- devOptIn, rulesFd_native, rulesOffset, rulesLength);
+ devOptInChars.c_str(), rulesFd_native, rulesOffset, rulesLength);
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -67,7 +68,7 @@
const JNINativeMethod g_methods[] = {
{ "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
{ "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
- { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;ZLjava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
+ { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
{ "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
{ "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
{ "setDebugLayersGLES", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayersGLES_native) },
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index f7dab42..81428dc 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -52,9 +52,9 @@
}
static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass,
- jlong tag, jstring nameStr, jint value) {
+ jlong tag, jstring nameStr, jlong value) {
withString(env, nameStr, [tag, value](char* str) {
- atrace_int(tag, str, value);
+ atrace_int64(tag, str, value);
});
}
@@ -106,7 +106,7 @@
// ----------- @FastNative ----------------
{ "nativeTraceCounter",
- "(JLjava/lang/String;I)V",
+ "(JLjava/lang/String;J)V",
(void*)android_os_Trace_nativeTraceCounter },
{ "nativeTraceBegin",
"(JLjava/lang/String;)V",
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 377e65c..102a0b7 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1137,7 +1137,7 @@
UniqueFile file = MakeUniqueFile(status_path.c_str(), "re");
char line[256];
- while (fgets(line, sizeof(line), file.get())) {
+ while (file != nullptr && fgets(line, sizeof(line), file.get())) {
jlong v;
if ( sscanf(line, "VmRSS: %" SCNd64 " kB", &v) == 1) {
rss[0] = v;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ea6e017..c745c16 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <system/graphics.h>
#include <ui/DisplayInfo.h>
+#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
@@ -97,6 +98,16 @@
jmethodID builder;
} gGraphicBufferClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDisplayedContentSampleClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDisplayedContentSamplingAttributesClassInfo;
+
// ----------------------------------------------------------------------------
static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -398,6 +409,73 @@
return javaObjectForIBinder(env, token);
}
+static jobject nativeGetDisplayedContentSamplingAttributes(JNIEnv* env, jclass clazz,
+ jobject tokenObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+
+ ui::PixelFormat format;
+ ui::Dataspace dataspace;
+ uint8_t componentMask;
+ status_t err = SurfaceComposerClient::getDisplayedContentSamplingAttributes(
+ token, &format, &dataspace, &componentMask);
+ if (err != OK) {
+ return nullptr;
+ }
+ return env->NewObject(gDisplayedContentSamplingAttributesClassInfo.clazz,
+ gDisplayedContentSamplingAttributesClassInfo.ctor,
+ format, dataspace, componentMask);
+}
+
+static jboolean nativeSetDisplayedContentSamplingEnabled(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jboolean enable, jint componentMask, jint maxFrames) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ return SurfaceComposerClient::setDisplayContentSamplingEnabled(
+ token, enable, componentMask, maxFrames);
+}
+
+static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobject tokenObj,
+ jlong maxFrames, jlong timestamp) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+
+ DisplayedFrameStats stats;
+ status_t err = SurfaceComposerClient::getDisplayedContentSample(
+ token, maxFrames, timestamp, &stats);
+ if (err != OK) {
+ return nullptr;
+ }
+
+ jlongArray histogramComponent0 = env->NewLongArray(stats.component_0_sample.size());
+ jlongArray histogramComponent1 = env->NewLongArray(stats.component_1_sample.size());
+ jlongArray histogramComponent2 = env->NewLongArray(stats.component_2_sample.size());
+ jlongArray histogramComponent3 = env->NewLongArray(stats.component_3_sample.size());
+ if ((histogramComponent0 == nullptr) ||
+ (histogramComponent1 == nullptr) ||
+ (histogramComponent2 == nullptr) ||
+ (histogramComponent3 == nullptr)) {
+ return JNI_FALSE;
+ }
+
+ env->SetLongArrayRegion(histogramComponent0, 0,
+ stats.component_0_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_0_sample.data()));
+ env->SetLongArrayRegion(histogramComponent1, 0,
+ stats.component_1_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_1_sample.data()));
+ env->SetLongArrayRegion(histogramComponent2, 0,
+ stats.component_2_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_2_sample.data()));
+ env->SetLongArrayRegion(histogramComponent3, 0,
+ stats.component_3_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_3_sample.data()));
+ return env->NewObject(gDisplayedContentSampleClassInfo.clazz,
+ gDisplayedContentSampleClassInfo.ctor,
+ stats.numFrames,
+ histogramComponent0,
+ histogramComponent1,
+ histogramComponent2,
+ histogramComponent3);
+}
+
static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj,
jboolean secure) {
ScopedUtfChars name(env, nameObj);
@@ -955,6 +1033,14 @@
(void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
(void*)nativeSetInputWindowInfo },
+ {"nativeGetDisplayedContentSamplingAttributes",
+ "(Landroid/os/IBinder;)Landroid/hardware/display/DisplayedContentSamplingAttributes;",
+ (void*)nativeGetDisplayedContentSamplingAttributes },
+ {"nativeSetDisplayedContentSamplingEnabled", "(Landroid/os/IBinder;ZII)Z",
+ (void*)nativeSetDisplayedContentSamplingEnabled },
+ {"nativeGetDisplayedContentSample",
+ "(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;",
+ (void*)nativeGetDisplayedContentSample },
};
int register_android_view_SurfaceControl(JNIEnv* env)
@@ -1009,6 +1095,18 @@
gGraphicBufferClassInfo.builder = GetStaticMethodIDOrDie(env, graphicsBufferClazz,
"createFromExisting", "(IIIIJ)Landroid/graphics/GraphicBuffer;");
+ jclass displayedContentSampleClazz = FindClassOrDie(env,
+ "android/hardware/display/DisplayedContentSample");
+ gDisplayedContentSampleClassInfo.clazz = MakeGlobalRefOrDie(env, displayedContentSampleClazz);
+ gDisplayedContentSampleClassInfo.ctor = GetMethodIDOrDie(env,
+ displayedContentSampleClazz, "<init>", "(J[J[J[J[J)V");
+
+ jclass displayedContentSamplingAttributesClazz = FindClassOrDie(env,
+ "android/hardware/display/DisplayedContentSamplingAttributes");
+ gDisplayedContentSamplingAttributesClassInfo.clazz = MakeGlobalRefOrDie(env,
+ displayedContentSamplingAttributesClazz);
+ gDisplayedContentSamplingAttributesClassInfo.ctor = GetMethodIDOrDie(env,
+ displayedContentSamplingAttributesClazz, "<init>", "(III)V");
return err;
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8e9a0bf..4aa88e7 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -102,6 +102,11 @@
MOUNT_EXTERNAL_FULL = 4,
};
+// Must match values in com.android.internal.os.Zygote.
+enum RuntimeFlags : uint32_t {
+ DEBUG_ENABLE_JDWP = 1,
+};
+
static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
std::ostringstream oss;
oss << __FILE__ << ":" << line << ": " << msg;
@@ -254,6 +259,38 @@
return true;
}
+static void EnableDebugger() {
+ // To let a non-privileged gdbserver attach to this
+ // process, we must set our dumpable flag.
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+ ALOGE("prctl(PR_SET_DUMPABLE) failed");
+ }
+
+ // A non-privileged native debugger should be able to attach to the debuggable app, even if Yama
+ // is enabled (see kernel/Documentation/security/Yama.txt).
+ if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == -1) {
+ // if Yama is off prctl(PR_SET_PTRACER) returns EINVAL - don't log in this
+ // case since it's expected behaviour.
+ if (errno != EINVAL) {
+ ALOGE("prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) failed");
+ }
+ }
+
+ // Set the core dump size to zero unless wanted (see also coredump_setup in build/envsetup.sh).
+ if (!GetBoolProperty("persist.zygote.core_dump", false)) {
+ // Set the soft limit on core dump size to 0 without changing the hard limit.
+ rlimit rl;
+ if (getrlimit(RLIMIT_CORE, &rl) == -1) {
+ ALOGE("getrlimit(RLIMIT_CORE) failed");
+ } else {
+ rl.rlim_cur = 0;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1) {
+ ALOGE("setrlimit(RLIMIT_CORE) failed");
+ }
+ }
+ }
+}
+
// The debug malloc library needs to know whether it's the zygote or a child.
extern "C" int gMallocLeakZygoteChild;
@@ -956,6 +993,11 @@
}
}
+ // Set process properties to enable debugging if required.
+ if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
+ EnableDebugger();
+ }
+
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index 2290b2f..bba8328 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -30,4 +30,5 @@
STOP_REASON_PREEMPT = 2;
STOP_REASON_TIMEOUT = 3;
STOP_REASON_DEVICE_IDLE = 4;
+ STOP_REASON_DEVICE_THERMAL = 5;
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3a908dc..514f306 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -38,6 +38,12 @@
// Unknown page. Should not be used in production code.
PAGE_UNKNOWN = 0;
+ // OPEN: Settings > Connected Devices > Bluetooth > (click on details link for a paired device)
+ BLUETOOTH_DEVICE_DETAILS = 1009;
+
+ // OPEN: Settings > Connected devices > Bluetooth > Pair new device
+ BLUETOOTH_PAIRING = 1018;
+
// OPEN: Settings homepage
SETTINGS_HOMEPAGE = 1502;
@@ -67,5 +73,14 @@
// OPEN: Settings > Privacy
TOP_LEVEL_PRIVACY = 1587;
+
+ // OPEN: Settings > Developer options > Disable > Info dialog
+ DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
+
+ // OPEN: WifiDppConfiguratorActivity (android.settings.WIFI_DPP_CONFIGURATOR_XXX action intents)
+ SETTINGS_WIFI_DPP_CONFIGURATOR = 1595;
+
+ // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
+ SETTINGS_WIFI_DPP_ENROLLEE = 1596;
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 0b9e347..a5350c9 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -34,6 +34,7 @@
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
+import "frameworks/base/core/proto/android/server/rolemanagerservice.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
import "frameworks/base/core/proto/android/service/battery.proto";
@@ -308,6 +309,11 @@
(section).userdebug_and_eng_only = true
];
+ optional com.android.server.role.RoleManagerServiceDumpProto role = 3024 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "role --proto"
+ ];
+
// Reserved for OEMs.
extensions 50000 to 100000;
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d917536..11bd43b 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -109,7 +109,15 @@
}
optional Autofill autofill = 140;
- optional SettingProto backup_agent_timeout_parameters = 18;
+ reserved 18; // Used to be backup_agent_timeout_parameters
+
+ message Backup {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto backup_agent_timeout_parameters = 1;
+ optional SettingProto backup_multi_user_enabled = 2;
+ }
+ optional Backup backup = 146;
message Battery {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -222,6 +230,13 @@
}
optional Connectivity connectivity = 32;
+ message ContentCapture {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto service_explicitly_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional ContentCapture content_capture = 145;
+
optional SettingProto contact_metadata_sync_enabled = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto contacts_database_wal_enabled = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -410,16 +425,20 @@
// Ordered GPU debug layer list for Vulkan
// i.e. <layer1>:<layer2>:...:<layerN>
optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
- // App will load ANGLE instead of native GLES drivers.
- optional SettingProto angle_enabled_app = 3;
+ // ANGLE - Force all PKGs to use ANGLE, regardless of any other settings
+ optional SettingProto angle_gl_driver_all_angle = 3;
+ // ANGLE - List of PKGs that specify an OpenGL driver
+ optional SettingProto angle_gl_driver_selection_pkgs = 4;
+ // ANGLE - Corresponding OpenGL driver selection for the PKG
+ optional SettingProto angle_gl_driver_selection_values = 5;
// App that can provide layer libraries.
- optional SettingProto debug_layer_app = 4;
+ optional SettingProto debug_layer_app = 6;
// Ordered GPU debug layer list for GLES
// i.e. <layer1>:<layer2>:...:<layerN>
- optional SettingProto debug_layers_gles = 5;
+ optional SettingProto debug_layers_gles = 7;
// App opt in to load updated graphics driver instead of
// native graphcis driver through developer options.
- optional SettingProto updated_gfx_driver_dev_opt_in_app = 6;
+ optional SettingProto updated_gfx_driver_dev_opt_in_app = 8;
}
optional Gpu gpu = 59;
@@ -628,6 +647,9 @@
// separated by commas.
optional SettingProto snooze_options = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto smart_replies_in_notifications_flags = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // Configuration options for smart replies and smart actions in notifications. This is
+ // encoded as a key=value list separated by commas.
+ optional SettingProto smart_suggestions_in_notifications_flags = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Notification notification = 82;
@@ -869,6 +891,8 @@
// Temperature at which the high temperature warning notification should
// be shown.
optional SettingProto warning_temperature_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // USB temperature at which the high temperature alarm notification should be shown.
+ optional SettingProto usb_alarm_temperature_level = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional TemperatureWarning temperature_warning = 119;
@@ -991,5 +1015,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 145;
+ // Next tag = 147;
}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ab50ad1..60561bd 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -185,6 +185,7 @@
optional int32 app_id = 5;
optional int32 isolated_app_id = 6;
optional bool persistent = 7;
+ optional int32 lru_index = 8;
}
message BroadcastRecordProto {
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index e83a2bf..7fe3be8 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -41,6 +41,7 @@
optional int64 last_heartbeat_time_millis = 16;
optional int64 next_heartbeat_time_millis = 17;
optional bool in_parole = 18;
+ optional bool in_thermal = 19;
repeated int32 started_users = 2;
@@ -215,6 +216,34 @@
// The fraction of a prefetch job's running window that must pass before
// we consider matching it against a metered network.
optional double conn_prefetch_relax_frac = 22;
+ // Whether to use heartbeats or rolling window for quota management. True
+ // will use heartbeats, false will use a rolling window.
+ optional bool use_heartbeats = 23;
+
+ message QuotaController {
+ // How much time each app will have to run jobs within their standby bucket window.
+ optional int64 allowed_time_per_period_ms = 1;
+ // How much time the package should have before transitioning from out-of-quota to in-quota.
+ // This should not affect processing if the package is already in-quota.
+ optional int64 in_quota_buffer_ms = 2;
+ // The quota window size of the particular standby bucket. Apps in this standby bucket are
+ // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ // WINDOW_SIZE_MS.
+ optional int64 active_window_size_ms = 3;
+ // The quota window size of the particular standby bucket. Apps in this standby bucket are
+ // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ // WINDOW_SIZE_MS.
+ optional int64 working_window_size_ms = 4;
+ // The quota window size of the particular standby bucket. Apps in this standby bucket are
+ // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ // WINDOW_SIZE_MS.
+ optional int64 frequent_window_size_ms = 5;
+ // The quota window size of the particular standby bucket. Apps in this standby bucket are
+ // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ // WINDOW_SIZE_MS.
+ optional int64 rare_window_size_ms = 6;
+ }
+ optional QuotaController quota_controller = 24;
}
message StateControllerProto {
@@ -357,6 +386,67 @@
}
repeated TrackedJob tracked_jobs = 2;
}
+ message QuotaController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional bool is_charging = 1;
+ optional bool is_in_parole = 2;
+
+ message TrackedJob {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional JobStatusShortInfoProto info = 1;
+ optional int32 source_uid = 2;
+ optional JobStatusDumpProto.Bucket effective_standby_bucket = 3;
+ optional bool has_quota = 4;
+ // The amount of time that this job has remaining in its quota. This
+ // can be negative if the job is out of quota.
+ optional int64 remaining_quota_ms = 5;
+ }
+ repeated TrackedJob tracked_jobs = 3;
+
+ message Package {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 user_id = 1;
+ optional string name = 2;
+ }
+
+ message TimingSession {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int64 start_time_elapsed = 1;
+ optional int64 end_time_elapsed = 2;
+ // The number of background jobs that ran during this session.
+ optional int32 bg_job_count = 3;
+ }
+
+ message Timer {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional Package pkg = 1;
+ // True if the Timer is actively tracking jobs.
+ optional bool is_active = 2;
+ // The time this timer last became active. Only valid if is_active is true.
+ optional int64 start_time_elapsed = 3;
+ // How many background jobs are currently running. Valid only if the device is_active
+ // is true.
+ optional int32 bg_job_count = 4;
+ // All of the jobs that the Timer is currently tracking.
+ repeated JobStatusShortInfoProto running_jobs = 5;
+ }
+
+ message PackageStats {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional Package pkg = 1;
+
+ optional Timer timer = 2;
+
+ repeated TimingSession saved_sessions = 3;
+ }
+ repeated PackageStats package_stats = 4;
+ }
message StorageController {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -403,8 +493,10 @@
ContentObserverController content_observer = 4;
DeviceIdleJobsController device_idle = 5;
IdleController idle = 6;
+ QuotaController quota = 9;
StorageController storage = 7;
TimeController time = 8;
+ // Next tag: 10
}
}
@@ -603,11 +695,13 @@
CONSTRAINT_CONNECTIVITY = 7;
CONSTRAINT_CONTENT_TRIGGER = 8;
CONSTRAINT_DEVICE_NOT_DOZING = 9;
+ CONSTRAINT_WITHIN_QUOTA = 10;
}
repeated Constraint required_constraints = 7;
repeated Constraint satisfied_constraints = 8;
repeated Constraint unsatisfied_constraints = 9;
optional bool is_doze_whitelisted = 10;
+ optional bool is_uid_active = 26;
message ImplicitConstraints {
// The device isn't Dozing or this job will be in the foreground. This
@@ -627,6 +721,7 @@
TRACKING_IDLE = 3;
TRACKING_STORAGE = 4;
TRACKING_TIME = 5;
+ TRACKING_QUOTA = 6;
}
// Controllers that are currently tracking the job.
repeated TrackingController tracking_controllers = 11;
@@ -660,6 +755,7 @@
NEVER = 4;
}
optional Bucket standby_bucket = 17;
+ optional bool is_exempted_from_app_standby = 27;
optional int64 enqueue_duration_ms = 18;
// Can be negative if the earliest runtime deadline has passed.
@@ -674,5 +770,5 @@
optional int64 internal_flags = 24;
- // Next tag: 26
+ // Next tag: 28
}
diff --git a/core/proto/android/server/rolemanagerservice.proto b/core/proto/android/server/rolemanagerservice.proto
new file mode 100644
index 0000000..3453a66
--- /dev/null
+++ b/core/proto/android/server/rolemanagerservice.proto
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.server.role;
+
+option java_multiple_files = true;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+message RoleManagerServiceDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // List of per-user states for all users.
+ repeated RoleUserStateProto user_states = 1;
+}
+
+message RoleUserStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // The user id of this state.
+ optional int32 user_id = 1;
+
+ // The version of this state.
+ optional int32 version = 2;
+
+ // The hash of packages for this state.
+ optional string packages_hash = 3;
+
+ // The set of roles in this state.
+ repeated RoleProto roles = 4;
+}
+
+message RoleProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // The name of this role, e.g. "android.app.role.DIALER".
+ optional string name = 1;
+
+ // The package names of the holders of this role.
+ repeated string holders = 2;
+}
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index 3d60a86..528c1a4 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -54,6 +54,9 @@
// Time attributes stored as an offset of the IntervalStats's beginTime.
optional int64 last_time_service_used_ms = 8;
optional int64 total_time_service_used_ms = 9;
+ // Time attributes stored as an offset of the IntervalStats's beginTime.
+ optional int64 last_time_visible_ms = 10;
+ optional int64 total_time_visible_ms = 11;
}
// Stores the relevant information an IntervalStats will have about a Configuration
@@ -82,6 +85,9 @@
optional string notification_channel = 12;
// notification_channel_index contains the index + 1 of the channel name in the string pool
optional int32 notification_channel_index = 13;
+ // If class field is an Activity, instance_id is a unique id of the
+ // Activity object.
+ optional int32 instance_id = 14;
}
// The following fields contain supplemental data used to build IntervalStats, such as a string
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 679a1d2..3767ed5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -211,7 +211,7 @@
repeated AppWindowTokenProto app_window_tokens = 3;
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
- optional .android.graphics.RectProto temp_inset_bounds = 6;
+ optional .android.graphics.RectProto displayed_bounds = 6;
optional bool defer_removal = 7;
optional int32 surface_width = 8;
optional int32 surface_height = 9;
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index ed040f4..f7dcee2 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -206,6 +206,8 @@
optional bool can_change_mode = 3;
optional bool can_change_power_role = 4;
optional bool can_change_data_role = 5;
+ optional int64 connected_at_millis = 6;
+ optional int64 last_connect_duration_millis = 7;
}
message UsbPortProto {
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 8fbea12..c7a6b68 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -44,14 +44,14 @@
SET_PERMISSION_GRANT_STATE = 19;
INSTALL_KEY_PAIR = 20;
INSTALL_CA_CERT = 21;
- ON_CHOOSE_KEY_ALIAS = 22;
+ CHOOSE_PRIVATE_KEY_ALIAS = 22;
REMOVE_KEY_PAIR = 23;
UNINSTALL_CA_CERTS = 24;
SET_CERT_INSTALLER_PACKAGE = 25;
SET_ALWAYS_ON_VPN_PACKAGE = 26;
SET_PERMITTED_INPUT_METHODS = 27;
SET_PERMITTED_ACCESSIBILITY_SERVICES = 28;
- SET_SCREEN_CAPTURE_DISABLE = 29;
+ SET_SCREEN_CAPTURE_DISABLED = 29;
SET_CAMERA_DISABLED = 30;
QUERY_SUMMARY_FOR_USER = 31;
QUERY_SUMMARY = 32;
@@ -64,16 +64,16 @@
SET_ORGANIZATION_COLOR = 39;
SET_PROFILE_NAME = 40;
SET_USER_ICON = 41;
- SET_DEVICE_OWNER_LOCKSCREEN_INFO = 42;
+ SET_DEVICE_OWNER_LOCK_SCREEN_INFO = 42;
SET_SHORT_SUPPORT_MESSAGE = 43;
SET_LONG_SUPPORT_MESSAGE = 44;
SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED = 45;
- SET_CROSS_PROFILE_CALLER_DISABLED = 46;
+ SET_CROSS_PROFILE_CALLER_ID_DISABLED = 46;
SET_BLUETOOTH_CONTACT_SHARING_DISABLED = 47;
ADD_CROSS_PROFILE_INTENT_FILTER = 48;
ADD_CROSS_PROFILE_WIDGET_PROVIDER = 49;
SET_SYSTEM_UPDATE_POLICY = 50;
- SET_LOCKTASK_PACKAGES = 51;
+ SET_LOCKTASK_MODE_ENABLED = 51;
ADD_PERSISTENT_PREFERRED_ACTIVITY = 52;
REQUEST_BUGREPORT = 53;
GET_WIFI_MAC_ADDRESS = 54;
@@ -99,13 +99,13 @@
INSTALL_SYSTEM_UPDATE_ERROR = 74;
IS_MANAGED_KIOSK = 75;
IS_UNATTENDED_MANAGED_KIOSK = 76;
- PROVISIONING_TO_COMP = 77;
- PROVISIONING_FORCED_DO = 78;
+ PROVISIONING_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 77;
+ PROVISIONING_PERSISTENT_DEVICE_OWNER = 78;
// existing Tron logs to be migrated to WestWorld
PROVISIONING_ENTRY_POINT_NFC = 79;
PROVISIONING_ENTRY_POINT_QR_CODE = 80;
- PROVISIONING_ENTRY_POINT_ZERO_TOUCH = 81;
+ PROVISIONING_ENTRY_POINT_CLOUD_ENROLLMENT = 81;
PROVISIONING_ENTRY_POINT_ADB = 82;
PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 83;
PROVISIONING_DPC_PACKAGE_NAME = 84;
@@ -134,4 +134,11 @@
PROVISIONING_TERMS_ACTIVITY_TIME_MS = 107;
PROVISIONING_TERMS_COUNT = 108;
PROVISIONING_TERMS_READ = 109;
+
+ SEPARATE_PROFILE_CHALLENGE_CHANGED = 110;
+ SET_GLOBAL_SETTING = 111;
+ PM_IS_INSTALLER_DEVICE_OWNER_OR_AFFILIATED_PROFILE_OWNER = 112;
+ PM_UNINSTALL = 113;
+ WIFI_SERVICE_ADD_NETWORK_SUGGESTIONS = 114;
+ WIFI_SERVICE_ADD_OR_UPDATE_NETWORK = 115;
}
diff --git a/core/proto/android/stats/docsui/docsui_enums.proto b/core/proto/android/stats/docsui/docsui_enums.proto
new file mode 100644
index 0000000..6cb606a
--- /dev/null
+++ b/core/proto/android/stats/docsui/docsui_enums.proto
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.stats.docsui;
+option java_multiple_files = true;
+
+enum LaunchAction {
+ UNKNOWN = 0;
+ OPEN = 1;
+ CREATE = 2;
+ GET_CONTENT = 3;
+ OPEN_TREE = 4;
+ PICK_COPY_DEST = 5;
+ BROWSE = 6;
+ OTHER = 7;
+}
+
+enum MimeType {
+ MIME_UNKNOWN = 0;
+ MIME_NONE = 1;
+ MIME_ANY = 2;
+ MIME_AUDIO = 3;
+ MIME_IMAGE = 4;
+ MIME_MESSAGE = 5;
+ MIME_MULTIPART = 6;
+ MIME_TEXT = 7;
+ MIME_VIDEO = 8;
+ MIME_OTHER = 9;
+}
+
+enum Root {
+ ROOT_UNKNOWN = 0;
+ ROOT_NONE = 1;
+ ROOT_OTHER_DOCS_PROVIDER = 2;
+ ROOT_AUDIO = 3;
+ ROOT_DEVICE_STORAGE = 4;
+ ROOT_DOWNLOADS = 5;
+ ROOT_HOME = 6;
+ ROOT_IMAGES = 7;
+ ROOT_RECENTS = 8;
+ ROOT_VIDEOS = 9;
+ ROOT_MTP = 10;
+ ROOT_THIRD_PARTY_APP = 11;
+}
+
+enum ContextScope {
+ SCOPE_UNKNOWN = 0;
+ SCOPE_FILES = 1;
+ SCOPE_PICKER = 2;
+}
+
+enum Provider {
+ PROVIDER_UNKNOWN = 0;
+ PROVIDER_SYSTEM = 1;
+ PROVIDER_EXTERNAL = 2;
+}
+
+enum FileOperation {
+ OP_UNKNOWN = 0;
+ OP_OTHER = 1;
+ OP_COPY = 2;
+ OP_COPY_INTRA_PROVIDER = 3;
+ OP_COPY_SYSTEM_PROVIDER = 4;
+ OP_COPY_EXTERNAL_PROVIDER = 5;
+ OP_MOVE = 6;
+ OP_MOVE_INTRA_PROVIDER = 7;
+ OP_MOVE_SYSTEM_PROVIDER = 8;
+ OP_MOVE_EXTERNAL_PROVIDER = 9;
+ OP_DELETE = 10;
+ OP_RENAME = 11;
+ OP_CREATE_DIR = 12;
+ OP_OTHER_ERROR = 13;
+ OP_DELETE_ERROR = 14;
+ OP_MOVE_ERROR = 15;
+ OP_COPY_ERROR = 16;
+ OP_RENAME_ERROR = 17;
+ OP_CREATE_DIR_ERROR = 18;
+ OP_COMPRESS_INTRA_PROVIDER = 19;
+ OP_COMPRESS_SYSTEM_PROVIDER = 20;
+ OP_COMPRESS_EXTERNAL_PROVIDER = 21;
+ OP_EXTRACT_INTRA_PROVIDER = 22;
+ OP_EXTRACT_SYSTEM_PROVIDER = 23;
+ OP_EXTRACT_EXTERNAL_PROVIDER = 24;
+ OP_COMPRESS_ERROR = 25;
+ OP_EXTRACT_ERROR = 26;
+}
+
+enum SubFileOperation {
+ SUB_OP_UNKNOWN = 0;
+ SUB_OP_QUERY_DOC = 1;
+ SUB_OP_QUERY_CHILD = 2;
+ SUB_OP_OPEN_FILE = 3;
+ SUB_OP_READ_FILE = 4;
+ SUB_OP_CREATE_DOC = 5;
+ SUB_OP_WRITE_FILE = 6;
+ SUB_OP_DELETE_DOC = 7;
+ SUB_OP_OBTAIN_STREAM_TYPE = 8;
+ SUB_OP_QUICK_MOVE = 9;
+ SUB_OP_QUICK_COPY = 10;
+}
+
+enum CopyMoveOpMode {
+ MODE_UNKNOWN = 0;
+ MODE_PROVIDER = 1;
+ MODE_CONVERTED = 2;
+ MODE_CONVENTIONAL = 3;
+}
+
+enum Authority {
+ AUTH_UNKNOWN = 0;
+ AUTH_OTHER = 1;
+ AUTH_MEDIA = 2;
+ AUTH_STORAGE_INTERNAL = 3;
+ AUTH_STORAGE_EXTERNAL = 4;
+ AUTH_DOWNLOADS = 5;
+ AUTH_MTP = 6;
+}
+
+enum UserAction {
+ ACTION_UNKNOWN = 0;
+ ACTION_OTHER = 1;
+ ACTION_GRID = 2;
+ ACTION_LIST = 3;
+ ACTION_SORT_NAME = 4;
+ ACTION_SORT_DATE = 5;
+ ACTION_SORT_SIZE = 6;
+ ACTION_SORT_TYPE = 7;
+ ACTION_SEARCH = 8;
+ ACTION_SHOW_SIZE = 9;
+ ACTION_HIDE_SIZE = 10;
+ ACTION_SETTINGS = 11;
+ ACTION_COPY_TO = 12;
+ ACTION_MOVE_TO = 13;
+ ACTION_DELETE = 14;
+ ACTION_RENAME = 15;
+ ACTION_CREATE_DIR = 16;
+ ACTION_SELECT_ALL = 17;
+ ACTION_SHARE = 18;
+ ACTION_OPEN = 19;
+ ACTION_SHOW_ADVANCED = 20;
+ ACTION_HIDE_ADVANCED = 21;
+ ACTION_NEW_WINDOW = 22;
+ ACTION_PASTE_CLIPBOARD = 23;
+ ACTION_COPY_CLIPBOARD = 24;
+ ACTION_DRAG_N_DROP = 25;
+ ACTION_DRAG_N_DROP_MULTI_WINDOW = 26;
+ ACTION_CUT_CLIPBOARD = 27;
+ ACTION_COMPRESS = 28;
+ ACTION_EXTRACT_TO = 29;
+ ACTION_VIEW_IN_APPLICATION = 30;
+ ACTION_INSPECTOR = 31;
+}
+
+enum InvalidScopedAccess {
+ SCOPED_DIR_ACCESS_UNKNOWN = 0;
+ SCOPED_DIR_ACCESS_INVALID_ARGUMENTS = 1;
+ SCOPED_DIR_ACCESS_INVALID_DIRECTORY = 2;
+ SCOPED_DIR_ACCESS_ERROR = 3;
+ SCOPED_DIR_ACCESS_DEPRECATED = 4;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 91b9869..77efbec 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -897,7 +897,7 @@
android:protectionLevel="dangerous"
android:usageInfoRequired="true" />
- <!-- @hide @SystemApi
+ <!-- @hide @SystemApi @TestApi
Allows an application to modify OBB files visible to other apps. -->
<permission android:name="android.permission.WRITE_OBB"
android:protectionLevel="signature|privileged" />
@@ -1120,6 +1120,18 @@
android:description="@string/permdesc_manageOwnCalls"
android:protectionLevel="normal" />
+ <!--Allows an app which implements the
+ {@link InCallService} API to be eligible to be enabled as a calling companion app. This
+ means that the Telecom framework will bind to the app's InCallService implementation when
+ there are calls active. The app can use the InCallService API to view information about
+ calls on the system and control these calls.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.CALL_COMPANION_APP"
+ android:label="@string/permlab_callCompanionApp"
+ android:description="@string/permdesc_callCompanionApp"
+ android:protectionLevel="normal" />
+
<!-- Allows a calling app to continue a call which was started in another app. An example is a
video calling app that wants to continue a voice call on the user's mobile network.<p>
When the handover of a call from one app to another takes place, there are two devices
@@ -1368,7 +1380,7 @@
<!-- ================================== -->
<eat-comment />
- <!-- @SystemApi Allows an application (Phone) to send a request to other applications
+ <!-- Allows an application (Phone) to send a request to other applications
to handle the respond-via-message action during incoming calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
@@ -1452,7 +1464,7 @@
android:description="@string/permdesc_accessLocationExtraCommands"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows an application to install a location provider into the Location Manager.
+ <!-- Allows an application to install a location provider into the Location Manager.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
android:protectionLevel="signature|privileged" />
@@ -1463,7 +1475,7 @@
<permission android:name="android.permission.HDMI_CEC"
android:protectionLevel="signature|privileged|vendorPrivileged" />
- <!-- @SystemApi Allows an application to use location features in hardware,
+ <!-- Allows an application to use location features in hardware,
such as the geofencing api.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.LOCATION_HARDWARE"
@@ -1595,7 +1607,7 @@
@hide This should only be used by ManagedProvisioning app.
-->
<permission android:name="android.permission.NETWORK_MANAGED_PROVISIONING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature" />
<!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces.
<p>Not for use by third-party applications. -->
@@ -1648,7 +1660,7 @@
@hide
-->
<permission android:name="android.permission.SUSPEND_APPS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|wellbeing" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
@@ -1658,7 +1670,7 @@
android:label="@string/permlab_bluetoothAdmin"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows applications to pair bluetooth devices without user interaction, and to
+ <!-- Allows applications to pair bluetooth devices without user interaction, and to
allow or disallow phonebook access or message access.
This is not available to third party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
@@ -1749,7 +1761,7 @@
android:usageInfoRequired="true" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
- <!-- @SystemApi Allows applications to call into AccountAuthenticators.
+ <!-- Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
android:protectionLevel="signature" />
@@ -1928,7 +1940,7 @@
<!-- =========================================== -->
<eat-comment />
- <!-- @SystemApi Allows modification of the telephony state - power on, mmi, etc.
+ <!-- Allows modification of the telephony state - power on, mmi, etc.
Does not include placing calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_PHONE_STATE"
@@ -2087,17 +2099,15 @@
<p>This permission should <em>only</em> be requested by the platform
document management app. This permission cannot be granted to
third-party apps.
- <p>Protection level: signature
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @hide Allows an application to cache content.
<p>Not for use by third-party applications.
- <p>Protection level: signature
-->
<permission android:name="android.permission.CACHE_CONTENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @SystemApi @hide
Allows an application to aggressively allocate disk space.
@@ -2128,6 +2138,15 @@
android:label="@string/permlab_disableKeyguard"
android:protectionLevel="normal" />
+ <!-- Allows an application to get the screen lock complexity and prompt users to update the
+ screen lock to a certain complexity level.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY"
+ android:label="@string/permlab_getAndRequestScreenLockComplexity"
+ android:description="@string/permdesc_getAndRequestScreenLockComplexity"
+ android:protectionLevel="normal" />
+
<!-- ================================== -->
<!-- Permissions to access other installed applications -->
<!-- ================================== -->
@@ -2171,6 +2190,13 @@
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to start an activity within its managed profile from
+ the personal profile.
+ This permission is not available to third party applications.
+ @hide -->
+ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
third party applications. -->
@@ -2206,9 +2232,9 @@
android:description="@string/permdesc_reorderTasks"
android:protectionLevel="normal" />
- <!-- @hide Allows an application to change to remove/kill tasks -->
+ <!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
@@ -2365,7 +2391,7 @@
<!-- ============================================ -->
<eat-comment />
- <!-- @SystemApi Allows applications to set the system time.
+ <!-- Allows applications to set the system time.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
android:protectionLevel="signature|privileged" />
@@ -2455,7 +2481,7 @@
<permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
android:protectionLevel="signature" />
- <!-- @SystemApi @TestApi Allows an application to modify the current configuration, such
+ <!-- Allows an application to modify the current configuration, such
as locale. -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
android:protectionLevel="signature|privileged|development" />
@@ -2477,7 +2503,7 @@
android:description="@string/permdesc_writeSettings"
android:protectionLevel="signature|preinstalled|appop|pre23" />
- <!-- @SystemApi Allows an application to modify the Google service map.
+ <!-- Allows an application to modify the Google service map.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_GSERVICES"
android:protectionLevel="signature|privileged" />
@@ -2493,7 +2519,7 @@
<permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Modify the global animation scaling factor.
+ <!-- Modify the global animation scaling factor.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ANIMATION_SCALE"
android:protectionLevel="signature|privileged|development" />
@@ -2548,12 +2574,12 @@
android:description="@string/permdesc_broadcastSticky"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows mounting and unmounting file systems for removable storage.
+ <!-- Allows mounting and unmounting file systems for removable storage.
<p>Not for use by third-party applications.-->
<permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows formatting file systems for removable storage.
+ <!-- Allows formatting file systems for removable storage.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
android:protectionLevel="signature|privileged" />
@@ -2587,7 +2613,7 @@
<permission android:name="android.permission.ASEC_RENAME"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows applications to write the apn settings and read sensitive fields of
+ <!-- Allows applications to write the apn settings and read sensitive fields of
an existing apn settings like user and password.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_APN_SETTINGS"
@@ -2688,40 +2714,40 @@
<!-- ========================================= -->
<eat-comment />
- <!-- @SystemApi Allows an application to read or write the secure system settings.
+ <!-- Allows an application to read or write the secure system settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_SECURE_SETTINGS"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to retrieve state dump information from system services.
+ <!-- Allows an application to retrieve state dump information from system services.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DUMP"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to read the low-level system log files.
+ <!-- Allows an application to read the low-level system log files.
<p>Not for use by third-party applications, because
Log entries can contain the user's private information. -->
<permission android:name="android.permission.READ_LOGS"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Configure an application for debugging.
+ <!-- Configure an application for debugging.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_DEBUG_APP"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to set the maximum number of (not needed)
+ <!-- Allows an application to set the maximum number of (not needed)
application processes that can be running.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_PROCESS_LIMIT"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to control whether activities are immediately
+ <!-- Allows an application to control whether activities are immediately
finished when put in the background.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ALWAYS_FINISH"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allow an application to request that a signal be sent to all persistent processes.
+ <!-- Allow an application to request that a signal be sent to all persistent processes.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
android:protectionLevel="signature|privileged|development" />
@@ -2731,7 +2757,7 @@
<!-- ==================================== -->
<eat-comment />
- <!-- @SystemApi Allows access to the list of accounts in the Accounts Service. -->
+ <!-- Allows access to the list of accounts in the Accounts Service. -->
<permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
android:protectionLevel="signature|privileged" />
@@ -2740,12 +2766,12 @@
<permission android:name="android.permission.GET_PASSWORD"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows applications to RW to diagnostic resources.
+ <!-- Allows applications to RW to diagnostic resources.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DIAGNOSTIC"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to open, close, or disable the status bar
+ <!-- Allows an application to open, close, or disable the status bar
and its icons.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.STATUS_BAR"
@@ -2770,7 +2796,7 @@
<permission android:name="android.permission.FORCE_BACK"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to update device statistics.
+ <!-- Allows an application to update device statistics.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
android:protectionLevel="signature|privileged" />
@@ -3047,12 +3073,20 @@
<permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by a android.service.intelligence.SmartSuggestionsService,
+ <!-- Must be required by a android.service.contentcapture.ContentCaptureService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
<p>Protection level: signature
-->
- <permission android:name="android.permission.BIND_SMART_SUGGESTIONS_SERVICE"
+ <permission android:name="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.service.autofill.augmented.AugmentedAutofillService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE"
android:protectionLevel="signature" />
<!-- Must be required by hotword enrollment application,
@@ -3209,7 +3243,7 @@
android:description="@string/permdesc_requestDeletePackages"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows an application to install packages.
+ <!-- Allows an application to install packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
android:protectionLevel="signature|privileged" />
@@ -3282,7 +3316,7 @@
<permission android:name="android.permission.FORCE_PERSISTABLE_URI_PERMISSIONS"
android:protectionLevel="signature" />
- <!-- @SystemApi Old permission for deleting an app's cache files, no longer used,
+ <!-- Old permission for deleting an app's cache files, no longer used,
but signals for us to quietly ignore calls instead of throwing an exception. -->
<permission android:name="android.permission.DELETE_CACHE_FILES"
android:protectionLevel="signature|privileged" />
@@ -3292,7 +3326,7 @@
<permission android:name="android.permission.INTERNAL_DELETE_CACHE_FILES"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to delete packages.
+ <!-- Allows an application to delete packages.
<p>Not for use by third-party applications.
<p>Starting in {@link android.os.Build.VERSION_CODES#N}, user confirmation is requested
when the application deleting the package is not the same application that installed the
@@ -3305,7 +3339,7 @@
<permission android:name="android.permission.MOVE_PACKAGE"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to change whether an application component (other than its own) is
+ <!-- Allows an application to change whether an application component (other than its own) is
enabled or not.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
@@ -3336,6 +3370,11 @@
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to observe role holder changes.
+ @hide -->
+ <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
+ android:protectionLevel="signature|installer" />
+
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
@@ -3387,6 +3426,13 @@
<permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to control display color transformations.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to collect usage infomation about brightness slider changes.
<p>Not for use by third-party applications.</p>
@hide
@@ -3421,7 +3467,7 @@
android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.CONTROL_VPN" />
- <!-- @SystemApi Allows an application to capture audio output.
+ <!-- Allows an application to capture audio output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
android:protectionLevel="signature|privileged" />
@@ -3459,7 +3505,7 @@
<permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to know what content is playing and control its playback.
+ <!-- Allows an application to know what content is playing and control its playback.
<p>Not for use by third-party applications due to privacy of media consumption</p> -->
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
android:protectionLevel="signature|privileged" />
@@ -3486,7 +3532,7 @@
<permission android:name="android.permission.BRICK"
android:protectionLevel="signature" />
- <!-- @SystemApi Required to be able to reboot the device.
+ <!-- Required to be able to reboot the device.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.REBOOT"
android:protectionLevel="signature|privileged" />
@@ -3547,11 +3593,11 @@
<permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Not for use by third-party applications. -->
+ <!-- Not for use by third-party applications. -->
<permission android:name="android.permission.MASTER_CLEAR"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to call any phone number, including emergency
+ <!-- Allows an application to call any phone number, including emergency
numbers, without going through the Dialer user interface for the user
to confirm the call being placed.
<p>Not for use by third-party applications. -->
@@ -3566,19 +3612,19 @@
<permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows enabling/disabling location update notifications from
+ <!-- Allows enabling/disabling location update notifications from
the radio.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows read/write access to the "properties" table in the checkin
+ <!-- Allows read/write access to the "properties" table in the checkin
database, to change values that get uploaded.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to collect component usage
+ <!-- Allows an application to collect component usage
statistics
<p>Declaring the permission implies intention to use the API and the user of the
device can grant permission through the Settings application. -->
@@ -3611,7 +3657,7 @@
android:description="@string/permdesc_requestIgnoreBatteryOptimizations"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows an application to collect battery statistics -->
+ <!-- Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|privileged|development" />
@@ -3640,12 +3686,12 @@
<permission android:name="android.permission.CONFIRM_FULL_BACKUP"
android:protectionLevel="signature" />
- <!-- @SystemApi Must be required by a {@link android.widget.RemoteViewsService},
+ <!-- Must be required by a {@link android.widget.RemoteViewsService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_REMOTEVIEWS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to tell the AppWidget service which application
+ <!-- Allows an application to tell the AppWidget service which application
can access AppWidget's data. The normal user flow is that a user
picks an AppWidget to go into a particular host, thereby giving that
host application access to the private data from the AppWidget app.
@@ -3676,7 +3722,7 @@
<permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"
android:protectionLevel="signature" />
- <!-- @SystemApi This permission can be used on content providers to allow the global
+ <!-- This permission can be used on content providers to allow the global
search system to access their data. Typically it used when the
provider has some permissions protecting it (which global search
would not be expected to hold), and added as a read-only permission
@@ -4071,10 +4117,10 @@
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
- <!-- Allows the holder to access and manage instant applications on the device.
- @hide -->
+ <!-- @SystemApi Allows the holder to access and manage instant applications on the device.
+ @hide -->
<permission android:name="android.permission.ACCESS_INSTANT_APPS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier|wellbeing" />
<uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
<!-- Allows the holder to view the instant applications on the device.
@@ -4170,6 +4216,11 @@
<permission android:name="android.permission.MANAGE_AUTO_FILL"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to manage the content capture service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_CONTENT_CAPTURE"
+ android:protectionLevel="signature" />
+
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->
@@ -4611,6 +4662,13 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.WallpaperUpdateReceiver"
+ android:permission="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY">
+ <intent-filter>
+ <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+ </intent-filter>
+ </receiver>
+
<service android:name="android.hardware.location.GeofenceHardwareService"
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
diff --git a/core/res/res/layout/notification_material_reply_text.xml b/core/res/res/layout/notification_material_reply_text.xml
index 71632a2..2b15dce 100644
--- a/core/res/res/layout/notification_material_reply_text.xml
+++ b/core/res/res/layout/notification_material_reply_text.xml
@@ -39,7 +39,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:visibility="gone"
- android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
<TextView
@@ -48,7 +48,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:visibility="gone"
- android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
<LinearLayout
@@ -64,7 +64,7 @@
android:layout_weight="1"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:layout_gravity="center"
- android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
<ProgressBar
android:id="@+id/notification_material_reply_progress"
diff --git a/core/res/res/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml
index c00acd5..be5d9b4 100644
--- a/core/res/res/layout/notification_template_ambient_header.xml
+++ b/core/res/res/layout/notification_template_ambient_header.xml
@@ -24,5 +24,5 @@
android:layout_height="wrap_content">
<include
layout="@layout/notification_template_header"
- android:theme="@style/Theme.Material.Notification.Ambient"/>
+ android:theme="@style/Theme.DeviceDefault.Notification.Ambient"/>
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 4bf1ad6..5ba1cf2 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -17,7 +17,7 @@
<!-- extends ViewGroup -->
<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/Theme.Material.Notification"
+ android:theme="@style/Theme.DeviceDefault.Notification"
android:id="@+id/notification_header"
android:orientation="horizontal"
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index c8864c2..2c6064e 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -24,7 +24,7 @@
android:paddingEnd="@dimen/notification_extra_margin_ambient"
>
<include layout="@layout/notification_template_ambient_header"
- android:theme="@style/Theme.Material.Notification.Ambient" />
+ android:theme="@style/Theme.DeviceDefault.Notification.Ambient" />
<LinearLayout
android:id="@+id/notification_action_list_margin_target"
@@ -51,8 +51,7 @@
android:orientation="vertical"
>
<TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.Material.Notification.Title"
- android:fontFamily="sans-serif"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_horizontal"
@@ -65,7 +64,7 @@
<TextView android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
android:singleLine="false"
android:layout_weight="1"
android:gravity="top|center_horizontal"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 26eb4e7..d5ea96f 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -53,7 +53,7 @@
android:layout_marginTop="@dimen/notification_progress_margin_top"
android:layout_marginBottom="6dp"/>
<com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_text_margin_top"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index d6f0e1d..eb89258 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -53,7 +53,7 @@
android:layout_marginTop="@dimen/notification_progress_margin_top"
android:layout_marginBottom="2dp"/>
<TextView android:id="@+id/inbox_text0"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -62,7 +62,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text1"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -71,7 +71,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text2"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -80,7 +80,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text3"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -89,7 +89,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text4"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -98,7 +98,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text5"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -107,7 +107,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text6"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 0717d96..483b479 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -34,7 +34,7 @@
android:orientation="vertical">
<com.android.internal.widget.ImageFloatingTextView
android:id="@+id/message_name"
- style="@style/Widget.Material.Notification.MessagingName"
+ style="@style/Widget.DeviceDefault.Notification.MessagingName"
android:layout_width="wrap_content"
android:textAlignment="viewStart"
/>
diff --git a/core/res/res/layout/notification_template_messaging_text_message.xml b/core/res/res/layout/notification_template_messaging_text_message.xml
index 3611186..26c081e 100644
--- a/core/res/res/layout/notification_template_messaging_text_message.xml
+++ b/core/res/res/layout/notification_template_messaging_text_message.xml
@@ -18,5 +18,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/message_text"
android:textAlignment="viewStart"
- style="@style/Widget.Material.Notification.MessagingText"
+ style="@style/Widget.DeviceDefault.Notification.MessagingText"
/>
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index 6459bb8..622f080 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -22,7 +22,7 @@
android:orientation="horizontal"
>
<TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.Material.Notification.Title"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
@@ -31,7 +31,7 @@
android:textAlignment="viewStart"
/>
<TextView android:id="@+id/text_line_1"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end|bottom"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 3b27666..01b14ae 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
<com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 8afbf66..266e846 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g>-Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi-oproepe | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g>-VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi-oproepe"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi-oproepe"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Af"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Verkies Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Verkies mobiel"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Laat die program toe om liggings in jou mediaversameling te lees."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Die program <xliff:g id="APP">%s</xliff:g> wil staaf."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometriese hardeware is nie beskikbaar nie"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nie herken nie"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelike vingerafdruk is bespeur. Probeer asseblief weer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kon nie vingerafdruk verwerk nie. Probeer asseblief weer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Vingerafdruksensor is vuil. Maak dit skoon en probeer weer."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vinger is te stadig beweer. Probeer asseblief weer."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nie herken nie"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Vingerafdruk is gestaaf"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Gesig is gestaaf"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Gesig is gestaaf; druk asseblief bevestig"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Laat \'n program toe om die sinkroniseringinstellings van \'n rekening te verander. Byvoorbeeld, dit kan gebruik word om sinkronisasie van die People-program met \'n ander rekening te aktiveer."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lees sinkroniseerstatistiek"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Laat \'n program toe om die sinkroniseringstatistieke van \'n rekening te lees, insluitend die geskiedenis van sinkroniseringgebeure en hoeveel data gesinkroniseer is."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"lees jou USB-berging se inhoud"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lees jou SD-kaart se inhoud"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Laat die program toe om die USB-geheue se inhoud te lees."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Laat die program toe om die inhoud van jou SD-kaart te lees."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"verander of vee die inhoud van jou USB-berging uit"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"Verander of vee die inhoud van jou SD-kaart uit"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Laat die program toe om die USB-geheue te skryf."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Laat die program toe om na die SD-kaart te skryf."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"lees jou gedeelde berging se inhoud"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Laat die program toe om jou gedeelde berging se inhoud te lees."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"verander of vee jou gedeelde berging se inhoud uit"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Laat die program toe om jou gedeelde berging se inhoud te skryf."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"maak en/of ontvang SIP-oproepe"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Laat die program toe om SIP-oproepe te maak en te ontvang."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registreer nuwe telekommunikasie-SIM-verbindings"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Skeduleer geleentheid vir gekose tyd"</string>
<string name="view_flight" msgid="7691640491425680214">"Spoor na"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Spoor gekose vlug na"</string>
+ <string name="translate" msgid="9218619809342576858">"Vertaal"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Vertaal geselekteerde teks"</string>
+ <string name="define" msgid="7394820043869954211">"Definieer"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definieer geselekteerde teks"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 passing</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Klaar"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Vee tans USB-geheue uit..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Vee tans SD-kaart uit..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Vee tans gedeelde berging uit …"</string>
<string name="share" msgid="1778686618230011964">"Deel"</string>
<string name="find" msgid="4808270900322985960">"Vind"</string>
<string name="websearch" msgid="4337157977400211589">"Websoektog"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-versoek is na USSD-versoek toe verander"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Na nuwe SS-versoek toe verander"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Werkprofiel"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Kennisgewing gegee"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Vou uit"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Vou in"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"wissel uitvou-aksie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b9be6af..f3b4df7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"የWiFi ጥሪ አደራረግ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"የWi-Fi ጥሪ"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"የWi-Fi ጥሪ"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ጠፍቷል"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi ተመርጧል"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"የተንቀሳቃሽ ስልክ ተመራጭ ነው"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"መተግበሪያው አካባቢዎችን ከሚዲያ ስብስብዎ እንዲያነብብ ያስችለዋል።"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"መተግበሪያ <xliff:g id="APP">%s</xliff:g> ማረጋገጥ ይፈልጋል"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ባዮሜትራዊ ሃርድዌር አይገኝም"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"አልታወቀም"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ከፊል የጣት አሻራ ተገኝቷል። እባክዎ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ጣት አሻራን መስራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"የጣት አሻራ ዳሳሽ ቆሽሿል። እባክዎ ያጽዱት እና እንደገና ይሞክሩ።"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ጣት ከልክ በላይ ተንቀራፎ ተንቀሳቅሷል። እባክዎ እንደገና ይሞክሩ።"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"አልታወቀም"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ፊት ተረጋግጧል"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ፊት ተረጋግጧል፣ እባክዎ አረጋግጥን ይጫኑ"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"መተግበሪያው የመለያ ማመሳሰል ቅንብሮችን እንዲያስተካክል ይፈቅድለታል። ለምሳሌ ይህ የሰዎች መተግበሪያን ከመለያ መመሳሰልን ለማንቃት ጥቅም ላይ ሊውል ይችላል።"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"የሥምሪያ ስታስቲክስ አንብብ"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"መተግበሪያው የማመሳሰል ክስተቶችን ታሪክ እና የተመሳሰለውን የውሂብ መጠን ጨምሮ የመለያን የማመሳሰል ስታትስቲክስ እንዲያነብ ይፈቅድለታል።"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"የUSB ማከማቻዎን ይዘቶች ያንብቡ"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"የSD ካርድህን ይዘቶች አንብብ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"መተግበሪያው የእርስዎ USB ማከማቻ ይዘቶችን እንዲያነብ ያስችለዋል።"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"መተግበሪያው የእርስዎ SD ካርድ ይዘቶችን እንዲያነብ ያስችለዋል።"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"የUSB ማከማቻዎን ይዘቶች ይቀይሩ ወይም ይሰርዙ"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"የSD ካርድህን ይዘቶች ቀይር ወይም ሰርዝ"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ወደ USB ማህደረ ትውስታው ለመፃፍ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"መተግበሪያውን ወደ SD ካርድ ለመፃፍ ይፈቅዳል።"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"የእርስዎን የተጋራ ማከማቻ ይዘቶችን ያንብብ"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"መተግበሪያው የእርስዎን የተጋራ ማከማቻ ይዘቶችን እንዲያነብ ያስችለዋል።"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"የተጋራ ማከማቻዎን ይዘቶች ይቀይሩ ወይም ይሰርዙ"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"መተግበሪያው የእርስዎን የተጋራ ማከማቻ ይዘቶችን እንዲጽፍ ያስችለዋል።"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"የSIP ጥሪዎችን ያድርጉ/ይቀበሉ"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"መተግበሪያው የSIP ጥሪዎችን እንዲያደር እና እንዲቀበል ያስችላል።"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"አዲስ የቴሌኮም ግንኙነቶችን መዝግብ"</string>
@@ -1090,7 +1086,7 @@
<string name="email" msgid="4560673117055050403">"ኢሜይል"</string>
<string name="email_desc" msgid="3638665569546416795">"ለተመረጡ አድራሻዎች ኢሜይል ላክ"</string>
<string name="dial" msgid="1253998302767701559">"ጥሪ"</string>
- <string name="dial_desc" msgid="6573723404985517250">"ወደተመረጠውን ስልክ ቁጥር ደውል"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"ወደ ተመረጠው ስልክ ቁጥር ደውል"</string>
<string name="map" msgid="5441053548030107189">"ካርታ"</string>
<string name="map_desc" msgid="1836995341943772348">"የተመረጠውን አድራሻ ያለበትን አግኝ"</string>
<string name="browse" msgid="1245903488306147205">"ክፈት"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ለተመረጠው ጊዜ የክስተት መርሐግብር ያስይዙ"</string>
<string name="view_flight" msgid="7691640491425680214">"ተከታተል"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"የተመረጠውን በረራ ይከታተሉ"</string>
+ <string name="translate" msgid="9218619809342576858">"ተርጉም"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"የተመረጠ ጽሑፍን ተርጉም"</string>
+ <string name="define" msgid="7394820043869954211">"ግለጽ"</string>
+ <string name="define_desc" msgid="7910883642444919726">"የተመረጠ ጽሑፍን ግለጽ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነጻ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> ከ<xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"ተከናውኗል"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB ማከማቻ በማጥፋት ላይ..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD ካርድ በማጥፋት ላይ..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"የተጋራ ማከማቻን በመደምሰስ ላይ…"</string>
<string name="share" msgid="1778686618230011964">"አጋራ"</string>
<string name="find" msgid="4808270900322985960">"አግኝ"</string>
<string name="websearch" msgid="4337157977400211589">"ድረ ፍለጋ"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"የSS ጥያቄ ወደ የUSSD ጥያቄ ተለውጧል"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"ወደ አዲስ የSS ጥያቄ ተለውጧል"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"የስራ መገለጫ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"ነቅተዋል"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ዘርጋ"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ሰብስብ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ዝርጋታን ቀያይር"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index ecc24ca..0789013 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -140,14 +140,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"شبكة Wi-Fi التابعة لـ <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"الاتصال عبر شبكة WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"شبكة VoWifi التابعة لـ <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"الاتصال عبر Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"الاتصال عبر WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"إيقاف"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"شبكة Wi-Fi مفضّلة"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"شبكة بيانات الجوال مفضَّلة"</string>
@@ -308,7 +304,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بتوجيه رسائل SMS وعرضها؟"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"التخزين"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"الوصول إلى الصور والوسائط والملفات على جهازك"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالدخول إلى الصور والوسائط والملفات على جهازك؟"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الصور والوسائط والملفات على جهازك؟"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"الميكروفون"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"تسجيل الصوت"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بتسجيل الصوت؟"</string>
@@ -545,6 +541,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"للسماح للتطبيق بقراءة المواقع من مجموعة الوسائط التابعة لك."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"يتطلَّب التطبيق <xliff:g id="APP">%s</xliff:g> المصادقة."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"معدّات المقاييس الحيوية غير متاحة."</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"لم يتم التعرف عليها."</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"تم اكتشاف بصمة الإصبع بشكل جزئي؛ يرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"جهاز استشعار بصمات الأصابع متسخ، يرجى تنظيفه وإعادة المحاولة."</string>
@@ -552,7 +553,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"تم تحريك الإصبع ببطء شديد. يُرجى إعادة المحاولة."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"لم يتم التعرف عليها."</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"تم مصادقة بصمة الإصبع"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"تمّت مصادقة الوجه"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string>
@@ -608,14 +608,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"للسماح للتطبيق بتعديل إعدادات المزامنة لحساب ما. على سبيل المثال، يمكن استخدام ذلك لتمكين مزامنة تطبيق \"الأشخاص\" مع حساب ما."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"قراءة إحصاءات المزامنة"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"للسماح للتطبيق بقراءة إحصائيات المزامنة لحساب ما، بما في ذلك سجل الأحداث المتزامنة ومقدار البيانات التي تمت مزامنتها."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"قراءة محتوى وحدة تخزين USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"قراءة محتوى بطاقة SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"للسماح للتطبيق بقراءة محتوى وحدة تخزين USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"للسماح للتطبيق بقراءة محتوى بطاقة SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"تعديل محتوى وحدة تخزين USB أو حذفها"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"تعديل محتوى بطاقة SD أو حذفها"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"للسماح للتطبيق بالكتابة إلى وحدة تخزين USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"للسماح للتطبيق بالكتابة إلى بطاقة SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"قراءة محتوى مساحة التخزين المشتركة"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"للسماح للتطبيق بقراءة محتوى مساحة التخزين المشتركة."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"تعديل محتوى مساحة التخزين المشتركة أو حذفه"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"للسماح للتطبيق بالكتابة إلى محتوى مساحة التخزين المشتركة."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"إجراء/تلقي مكالمات SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"للسماح للتطبيق بإجراء مكالمات SIP وتلقيها."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"تسجيل اتصالات SIM اللاسلكية الجديدة"</string>
@@ -1185,6 +1181,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"جدولة الحدث في الوقت المختار"</string>
<string name="view_flight" msgid="7691640491425680214">"تتبّع"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"تتبُّع الرحلة الجوية المختارة"</string>
+ <string name="translate" msgid="9218619809342576858">"ترجمة"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ترجمة النص المُختار"</string>
+ <string name="define" msgid="7394820043869954211">"تعريف"</string>
+ <string name="define_desc" msgid="7910883642444919726">"تحديد النص المُختار"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ليست هناك مساحة تخزين كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
@@ -1552,8 +1552,7 @@
<item quantity="one">مباراة واحدة</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"تم"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"جارٍ محو وحدة تخزين USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"جارٍ محو بطاقة SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"جارٍ محو بيانات مساحة التخزين المشتركة…"</string>
<string name="share" msgid="1778686618230011964">"مشاركة"</string>
<string name="find" msgid="4808270900322985960">"بحث"</string>
<string name="websearch" msgid="4337157977400211589">"بحث الويب"</string>
@@ -1965,8 +1964,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"تم تغيير طلب SS إلى طلب USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"تم التغيير إلى طلب SS الجديد."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"الملف الشخصي للعمل"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"تمّ تفعيل التنبيه"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"توسيع"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"تصغير"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"تبديل التوسيع"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index b897e24..f716579 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> ৱাই-ফাই"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"ৱাই- ফাই কলিং | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"ৱাই-ফাই কলিং"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"ৱাই-ফাই"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"ৱাই-ফাই কলিং"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"অফ হৈ আছে"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"ৱাই-ফাইক অগ্ৰাধিকাৰ দিয়া হৈছে"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ম\'বাইলক অগ্ৰাধিকাৰ দিয়া হৈছে"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"এপক আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ দিয়ে।"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> এপে বিশ্বাসযোগ্যতাৰ প্ৰমাণ কৰিব বিচাৰে।"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্ৰিক হাৰ্ডৱেৰ উপলব্ধ নহয়"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"চিনাক্ত কৰিব পৰা নাই"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ফিংগাৰপ্ৰিণ্ট আংশিকভাৱে চিনাক্ত কৰা হৈছে। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ফিগাৰপ্ৰিণ্টৰ প্ৰক্ৰিয়া সম্পাদন কৰিবপৰা নগ\'ল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো লেতেৰা হৈ আছে। অনুগ্ৰহ কৰি পৰিষ্কাৰ কৰি আকৌ চেষ্টা কৰক।"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"আঙুলিৰ গতি অতি মন্থৰ আছিল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"চিনাক্ত কৰিব পৰা নাই"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ\'ল"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ\'ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণ স্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ছিংকৰ পৰিসংখ্যা পঢ়ক"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ছিংকৰ কাৰ্যক্ৰমসমূহৰ ইতিহাস আৰু ছিংক কৰা ডেটাৰ পৰিমাণসহ কোনো একাউণ্টৰ ছিংকৰ তথ্য পঢ়িবলৈ এপক অনুমতি দিয়ে।"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"আপোনাৰ ইউএছবি সঞ্চয়াগাৰৰ সমলবোৰ পঢ়ক"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"আপোনাৰ এছডি কাৰ্ডৰ সমলবোৰ পঢ়ক"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"আপোনাৰ ইউএছবি সঞ্চয়াগাৰৰ সমলসমূহ পঢ়িবলৈ এপক অনুমতি দিয়ে।"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"আপোনাৰ এছডি কাৰ্ডৰ সমল পঢ়িবলৈ এপক অনুমতি দিয়ে।"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"আপোনাৰ ইউএছবি সঞ্চয়াগাৰৰ সমলবোৰ সংশোধন কৰক বা মচক"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"আপোনাৰ এছডি কাৰ্ডৰ সমলবোৰ সংশোধন কৰক বা মচক"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"এপটোক ইউএছবি সঞ্চয়াগাৰত লিখাৰ অনুমতি দিয়ে।"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"এপটোক এছডি কাৰ্ডত লিখাৰ অনুমতি দিয়ে।"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা সঞ্চয়াগাৰৰ সমল পঢ়িব পাৰে"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা সঞ্চয়াগাৰৰ সমল পঢ়িবলৈ এপটোক অনুমতি দিয়ে।"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা সঞ্চয়াগাৰৰ সমল সংশোধন কৰিব বা মচিব পাৰে"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা সঞ্চয়াগাৰৰ সমল লিখিবলৈ এপটোক অনুমতি দিয়ে।"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP কল কৰা/পোৱা"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"এপটোক SIP কলসমূহ কৰিবলৈ আৰু পাবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"নতুন টেলিকম ছিম সংযোগসমূহ পঞ্জীয়ন কৰা"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"বাছনি কৰা সময়ৰ বাবে কাৰ্যক্ৰমৰ তালিকা বনাওক"</string>
<string name="view_flight" msgid="7691640491425680214">"ট্ৰেক কৰক"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"বাছনি কৰা বিমানটো ট্ৰেক কৰক"</string>
+ <string name="translate" msgid="9218619809342576858">"অনুবাদ কৰক"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"বাছনি কৰা পাঠ অনুবাদ কৰক"</string>
+ <string name="define" msgid="7394820043869954211">"সংজ্ঞা দিব পাৰে"</string>
+ <string name="define_desc" msgid="7910883642444919726">"বাছনি কৰা পাঠৰ সংজ্ঞা দিব পাৰে"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"সঞ্চয়াগাৰৰ খালী ঠাই শেষ হৈ আছে"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ছিষ্টেমৰ কিছুমান কাৰ্যকলাপে কাম নকৰিবও পাৰে"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ছিষ্টেমৰ বাবে পৰ্যাপ্ত খালী ঠাই নাই। আপোনাৰ ২৫০এম. বি. খালী ঠাই থকাটো নিশ্চিত কৰক আৰু ৰিষ্টাৰ্ট কৰক।"</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g>ৰ <xliff:g id="INDEX">%d</xliff:g>টা</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"সম্পন্ন হ\'ল"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"ইউএছবি সঞ্চয়াগাৰৰ ডেটা মচি থকা হৈছে…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"এছডি কাৰ্ডৰ ডেটা মচি থকা হৈছে…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"শ্বেয়াৰ কৰি থোৱা সঞ্চয়াগাৰ মচি থকা হৈছে…"</string>
<string name="share" msgid="1778686618230011964">"শ্বেয়াৰ কৰক"</string>
<string name="find" msgid="4808270900322985960">"বিচাৰক"</string>
<string name="websearch" msgid="4337157977400211589">"ৱেবত সন্ধান কৰক"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS অনুৰোধ USSD অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"নতুন SS অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"সতৰ্ক কৰা হ’ল"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"বিস্তাৰ কৰক"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"সংকুচিত কৰক"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"সম্প্ৰসাৰণ ট’গল কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 3f3d685..55966cf 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi Zəngi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi Zəngi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WiFi Zəngi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Deaktiv"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi tərcih edilir"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobil tərcih"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tətbiqin media kolleksiyanızdan məkanları oxumasına icazə verin."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> doğrulamaq istəyir."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrik proqram əlçatan deyil"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmır"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmaq qismən müəyyən olundu. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmaq izi tanınmadı. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmaq izi sensoru çirklidir. Lütfən, təmizləyin və yenidən cəhd edin."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Barmağınızı çox yavaş hərəkət etdirdiniz. Lütfən, yenidən cəhd edin."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmır"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Barmaq izi doğrulandı"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Üz doğrulandı"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Üz təsdiq edildi, təsdiq düyməsinə basın"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Tətbiqə hesab üçün sinxronizasiya nizamlarını dəyişməyə icazə verir. Məsələn, bu istifadəçi hesablı Şəxslər tətbiqinin sinxronizasiyasını başlamaq üçün istifadə edilə bilər."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"sinxronizasiya statistikasını oxumaq"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Tətbiqə sync tədbirlərinin tarixçəsi və nə qədər datanın sinx olduğu da daxil olmaqla bərabər, hər hansı bir hesab üçün olan sinx statlarını oxumağa imkan verir."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB yaddaşınızın kontentini oxuyun"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD kartınızın kontentini oxuyun"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Tətbiqə USB yaddaşdan kontent oxuma icazəsi verir."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Tətbiqə SD kartınızın məzmununu oxumağa imkan verir."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB yaddaşınızın məzmununu dəyişmək və ya silmək"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD kart kontentlərini dəyişir və ya silir"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Tətbiqə USB yaddaşa yazmağa imkan verir."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tətbiqə SD karta yazma icazəsi verir."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"paylaşılan yaddaşdakı kontenti oxumaq"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Tətbiqə paylaşılan yaddaşdakı kontenti oxumaq imkanı verir."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"paylaşılan yaddaşdakı kontenti dəyişmək və ya silmək"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Tətbiqə paylaşılan yaddaşdakı kontenti yazmaq imkanı verir."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP çağrıları göndərin/qəbul edin"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Tətbiqə SIP çağrılarını göndərməyə və qəbul etməyə icazə verin."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"yeni telekom bağlantılarını qeydiyyata alın"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Tədbiri seçilmiş vaxta planlaşdırın"</string>
<string name="view_flight" msgid="7691640491425680214">"Trek"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seçilmiş uçuşu izləyin"</string>
+ <string name="translate" msgid="9218619809342576858">"Tərcümə edin"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Seçilmiş mətni tərcümə edin"</string>
+ <string name="define" msgid="7394820043869954211">"Təyin edin"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Seçilmiş mətni təyin edin"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Yaddaş yeri bitir"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bəzi sistem funksiyaları işləməyə bilər"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 eynilik</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Hazırdır"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB yaddaş silinir..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD kart silinir..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Paylaşılan yaddaş silinir…"</string>
<string name="share" msgid="1778686618230011964">"Paylaşın"</string>
<string name="find" msgid="4808270900322985960">"Tapın"</string>
<string name="websearch" msgid="4337157977400211589">"Veb Axtarış"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS sorğusu USSD sorğusuna dəyişdirildi"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Yeni SS sorğusuna dəyişdirildi"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"İş profili"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Xəbərdarlıq edildi"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Genişləndirin"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Yığcamlaşdırın"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"keçid genişlənməsi"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ca2c823..9c45ba8 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -137,14 +137,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Pozivanje preko Wi-Fi-ja | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Pozivanje preko Wi-Fi-ja"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Pozivanje preko Wi-Fi-ja"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Isključeno"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Prednost ima Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Želim mobilne podatke"</string>
@@ -536,6 +532,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Dozvoljava aplikaciji da čita lokacije iz medijske kolekcije."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da potvrdi vaš identitet."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je delimični otisak prsta. Probajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nije uspela obrada otiska prsta. Probajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor za otiske prstiju je prljav. Očistite ga i pokušajte ponovo."</string>
@@ -543,7 +544,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Previše sporo ste pomerili prst. Probajte ponovo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisak prsta je potvrđen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Lice je potvrđeno"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Lice je potvrđeno. Pritisnite Potvrdi"</string>
@@ -599,14 +599,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Dozvoljava aplikaciji da menja podešavanja sinhronizacije za nalog. Na primer, ovako može da se omogući sinhronizacija aplikacije Ljudi sa nalogom."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"čitanje statistike o sinhronizaciji"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Dozvoljava aplikaciji da čita statistiku sinhronizacije za nalog, uključujući istoriju sinhronizovanih događaja i količinu podataka koji se sinhronizuju."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"čitanje sadržaja USB memorije"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"čitanje sadržaja SD kartice"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Omogućava aplikaciji da čita sadržaj USB memorije."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Omogućava aplikaciji da čita sadržaj SD kartice."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"izmena ili brisanje sadržaja USB memorije"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"menjanje ili brisanje sadržaja SD kartice"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Dozvoljava aplikaciji da upisuje podatke na USB memoriju."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Dozvoljava aplikaciji da upisuje podatke na SD karticu."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"čitanje sadržaja deljenog memorijskog prostora"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Dozvoljava aplikaciji da čita sadržaj deljenog memorijskog prostora."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"menjanje ili brisanje sadržaja deljenog memorijskog prostora"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Dozvoljava aplikaciji da upisuje sadržaj deljenog memorijskog prostora."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"upućivanje/prijem SIP poziva"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Omogućava aplikaciji da upućuje i prima SIP pozive."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registruje nove veze sa telekomunikacionim mrežama preko SIM kartice"</string>
@@ -1125,6 +1121,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakažite događaj u izabrano vreme"</string>
<string name="view_flight" msgid="7691640491425680214">"Prati"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Pratite izabrani let"</string>
+ <string name="translate" msgid="9218619809342576858">"Prevedi"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevedite izabrani tekst"</string>
+ <string name="define" msgid="7394820043869954211">"Definiši"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definišite izabrani tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memorijski prostor je na izmaku"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda ne funkcionišu"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
@@ -1483,8 +1483,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> od <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Gotovo"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Brisanje USB memorije..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Brisanje SD kartice..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Briše se deljeni memorijski prostor…"</string>
<string name="share" msgid="1778686618230011964">"Deli"</string>
<string name="find" msgid="4808270900322985960">"Pronađi"</string>
<string name="websearch" msgid="4337157977400211589">"Veb-pretraga"</string>
@@ -1866,8 +1865,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS zahtev je promenjen u USSD zahtev"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Promenjeno je u novi SS zahtev"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil za Work"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Obavešteno"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Proširi"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Skupi"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"uključite/isključite proširenje"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 3462a84..930feba 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi ад <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi-тэлефанія | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWi-Fi ад <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi-тэлефанія"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi-тэлефанія"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWi-Fi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Выкл."</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Прыярытэт Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Прыярытэт мабільнай сеткі"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Праграма зможа паказваць месцазнаходжанне ў калекцыі мультымедыя."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" патрабуе аўтэнтыфікацыі."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Біяметрычнае абсталяванне недаступнае"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не распазнана"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Выяўлена частка адбіткаў пальцаў. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не атрымалася апрацаваць адбітак пальца. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчык адбіткаў пальцаў брудны. Ачысціце яго і паспрабуйце яшчэ раз."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Палец рухаўся занадта павольна. Паспрабуйце яшчэ раз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Не распазнана"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Адбітак пальца распазнаны"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Твар распазнаны"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Твар распазнаны. Націсніце, каб пацвердзіць"</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Дазваляе прыкладанню змяняць налады сінхранізацыі для ўліковага запісу. Напрыклад, гэта можа выкарыстоўвацца для ўключэння сінхранізацыі прыкладання \"Кантакты\" з уліковым запісам."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"чытаць статыстыку сінхранізацыі"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Дазваляе прыкладанням чытаць статыстыку сінхранізацыі для ўліковага запісу, у тым ліку гісторыю сінхранізацыі мерапрыемстваў і наколькі сінхранізаваны дадзеныя."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"чытанне змесціва USB-назапашв."</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"чытанне змесціва SD-карты"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Дазваляе праграме счытваць змесціва вашага USB-сховішча."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Дазваляе праграме счытваць змесціва вашай SD-карты."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"зм. або выд. змес. USB-назап."</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"змяніць або выдаліць змесціва SD-карты"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Дазваляе прыкладаням выконваць запіс ва USB-назапашвальнік."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дазваляе прыкладанням запісваць на SD-карту."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"чытанне змесціва абагуленага сховішча"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Дазваляе праграме счытваць змесціва абагуленага сховішча."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"змяненне або выдаленне змесціва абагуленага сховішча"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Дазваляе праграме запісваць змесціва абагуленага сховішча."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"ажыццяўленне/прыманне выклікаў SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Дазваляе праграме рабіць і прымаць выклікі SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"рэгістрацыя новых падлучэнняў сувязі SIM"</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Запланаваць падзею ў выбраны час"</string>
<string name="view_flight" msgid="7691640491425680214">"Сачыць"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Адсочваць рэйс"</string>
+ <string name="translate" msgid="9218619809342576858">"Перакласці"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Перакласці вылучаны тэкст"</string>
+ <string name="define" msgid="7394820043869954211">"Вызначыць"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Вызначыць вылучаны тэкст"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string>
@@ -1506,8 +1506,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> з <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Гатова"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Выдаленне дадзеных з USB-назапашвальнiка..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Выдаленне дадзеных з SD-карты..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Сціраюцца даныя абагуленага сховішча…"</string>
<string name="share" msgid="1778686618230011964">"Абагуліць"</string>
<string name="find" msgid="4808270900322985960">"Пошук"</string>
<string name="websearch" msgid="4337157977400211589">"Вэб-пошук"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-запыт заменены на USSD-запыт"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Зроблена замена на новы SS-запыт"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Працоўны профіль"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"З гукам"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Разгарнуць"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Згарнуць"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"разгарнуць/згарнуць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 6693dfd..d2857e8 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi от <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Обаждания през Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWi-Fi от <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Обаждания през Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Обаждания през Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Изключено"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Предпочита се Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Предпочитат се мобилни данни"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Разрешава на приложението да чете местоположенията от мултимедийната ви колекция."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Приложението <xliff:g id="APP">%s</xliff:g> изисква удостоверяване."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометричният хардуер не е налице"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не е разпознато"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Открит е частичен отпечатък. Моля, опитайте отново."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатъкът не можа да се обработи. Моля, опитайте отново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорът за отпечатъци е мръсен. Моля, почистете го и опитайте отново."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Преместихте пръста си твърде бавно. Моля, опитайте отново."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Не е разпознато"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечатъкът е удостоверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Лицето е удостоверено"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Лицето е удостоверено. Моля, натиснете „Потвърждаване“"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Разрешава на приложението да чете настройките за синхронизиране на профил. Например това може да бъде използвано за активиране на синхронизирането на приложението Хора с даден профил."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"четене на статистическите данни за синхронизиране"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Разрешава на приложението да чете статистическите данни за синхронизирането на профил, включително историята на синхронизираните събития и обема на информацията, която се синхронизира."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"четене на съдърж. от USB хран. ви"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"четене на съдържанието от SD картата ви"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Разрешава на прил. да чете съдърж. на USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Разрешава на приложението да чете съдържанието на SD картата ви."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"промяна или изтрив. на съдърж. от USB хран. ви"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"промяна или изтриване на съдържанието от SD картата ви"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Разрешава на приложението да записва в USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Разрешава на приложението да записва върху SD картата."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"четене на съдърж. от сподел. ви хранил."</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Разрешава на прил. да чете съдърж. от споделеното ви хранилище."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"промяна или изтрив. на съдърж. от сподел. ви хранил."</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Разрешава на прил. да записва съдърж. от сподел. ви хранил."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"извършване/получаване на обаждания чрез SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Разрешава на приложението да извършва и получава обаждания чрез SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"регистриране на нови телекомуникационни връзки за SIM карти"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Насрочване на събитие за избраната дата"</string>
<string name="view_flight" msgid="7691640491425680214">"Проследяване"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Проследяване на избрания полет"</string>
+ <string name="translate" msgid="9218619809342576858">"Превод"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Превод на избрания текст"</string>
+ <string name="define" msgid="7394820043869954211">"Определение"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Определение на избрания текст"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Мястото в хранилището е на изчерпване"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Възможно е някои функции на системата да не работят"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 игра</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Готово"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB хранилището се изтрива..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD картата се изтрива..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Споделеното хранилище се изтрива…"</string>
<string name="share" msgid="1778686618230011964">"Споделяне"</string>
<string name="find" msgid="4808270900322985960">"Намиране"</string>
<string name="websearch" msgid="4337157977400211589">"Уеб търсене"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS заявката е променена на USSD заявка"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Променено на нова SS заявка"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Служебен потребителски профил"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Сигналът е изпратен"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Разгъване"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Свиване"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"превключване на разгъването"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 120abf0..80eefc4 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> ওয়াই-ফাই"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"ওয়াই-ফাই কলিং | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"ওয়াই-ফাই কলিং"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"ওয়াই-ফাই"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"ওয়াই-ফাই কলিং"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"বন্ধ আছে"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"পছন্দের ওয়াই-ফাই"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"পছন্দের মোবাইল"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"আপনার মিডিয়া সংগ্রহ থেকে লোকেশন দেখতে অ্যাপকে অনুমতি দিন।"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> অ্যাপ্লিকেশন যাচাই করতে চাইছে।"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"স্বীকৃত নয়"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"আঙ্গুলের ছাপ আংশিক শনাক্ত করা হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"আঙ্গুলের ছাপ প্রক্রিয়া করা যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"আঙ্গুলের ছাপ নেওয়ার সেন্সরটি অপরিস্কার৷ অনুগ্রহ করে পরিষ্কার করে আবার চেষ্টা করুন৷"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"আঙ্গুল খুব ধীরে সরানো হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"স্বীকৃত নয়"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ফেস যাচাই করা হয়েছে"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ফেস যাচাই করা হয়েছে, \'কনফার্ম করুন\' বোতাম প্রেস করুন"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"একটি অ্যাকাউন্টের জন্য সমন্বয় সেটিংস সংশোধন করতে একটি অ্যাপ্লিকেশানকে অনুমতি দেয়৷ উদাহরণস্বরূপ, এটি একটি অ্যাকাউন্টের সাথে People অ্যাপ্লিকেশানের সমন্বয় সক্ষম করার কাজে ব্যবহৃত হতে পারে৷"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"সিঙ্ক পরিসংখ্যান পড়ে"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"অ্যাপ্লিকেশানটিকে একটি অ্যাকাউন্টের জন্য কতটা ডেটা সিঙ্ক হয়েছে এবং সিঙ্ক করা ইভেন্টের ইতিহাস সহ সিঙ্কের স্থিতি পড়ার অনুমতি দেয়৷"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"আপনার USB সংগ্রহস্থলের সামগ্রীগুলিকে পড়ুন"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"আপনার SD কার্ডের সামগ্রীগুলিকে পড়ুন"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"অ্যাপ্লিকেশানটিকে আপনার USB সঞ্চয়স্থানের সামগ্রীগুলিকে পড়ার অনুমতি দেয়৷"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"অ্যাপ্লিকেশানটিকে আপনার SD কার্ডের সামগ্রীগুলিকে পড়ার অনুমতি দেয়৷"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"আপনার USB সংগ্রহস্থলের সামগ্রীগুলিকে মুছুন বা সংশোধন করুন"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"আপনার SD কার্ডের সামগ্রীগুলিকে মুছুন বা সংশোধন করুন"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"অ্যাপ্লিকেশানকে USB সঞ্চয়স্থানে লেখার অনুমতি দেয়৷"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"অ্যাপ্লিকেশানকে SD কার্ডে লেখার অনুমতি দেয়৷"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"শেয়ার করা স্টোরেজের কন্টেন্ট পড়ুন"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"শেয়ার করা স্টোরেজের কন্টেন্ট পড়ার জন্য অ্যাপটিকে অনুমতি দিন।"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"শেয়ার করা স্টোরেজের কন্টেন্ট মুছে ফেলুন বা পরিবর্তন করুন"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"শেয়ার করা স্টোরেজের কন্টেন্ট লেখার জন্য অ্যাপটিকে অনুমতি দিন।"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP কল করুন/গ্রহণ করুন"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"অ্যাপ্লিকেশানকে SIP কল করতে ও গ্রহণ করতে অনুমতি দেয়।"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"নতুন টেলিকম সিম সংযোগগুলির নিবন্ধন"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"বেছে নেওয়া সময়ে ইভেন্ট সেট করুন"</string>
<string name="view_flight" msgid="7691640491425680214">"ট্র্যাক"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"বেছে নেওয়া ফ্লাইট ট্র্যাক করুন"</string>
+ <string name="translate" msgid="9218619809342576858">"অনুবাদ করুন"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"বেছে নেওয়া টেক্সট অনুবাদ করুন"</string>
+ <string name="define" msgid="7394820043869954211">"ব্যাখ্যা দিন"</string>
+ <string name="define_desc" msgid="7910883642444919726">"বেছে নেওয়া টেক্সটের ব্যাখ্যা দিন"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g>টির <xliff:g id="INDEX">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"সম্পন্ন হয়েছে"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB সংগ্রহস্থল মোছা হচ্ছে…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD কার্ড মোছা হচ্ছে…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"শেয়ার করা স্টোরেজ মুছে ফেলা হচ্ছে…"</string>
<string name="share" msgid="1778686618230011964">"শেয়ার করুন"</string>
<string name="find" msgid="4808270900322985960">"খুঁজুন"</string>
<string name="websearch" msgid="4337157977400211589">"ওয়েব সার্চ"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS অনুরোধ USSD অনুরোধে পরিবর্তন করা হয়েছে"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"নতুন SS অনুরোধে পরিবর্তন করা হয়েছে"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"কর্মস্থলের প্রোফাইল"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"সতর্ক করা হয়েছে"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"বড় করুন"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"সঙ্কুচিত করুন"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"টগল সম্প্রসারণ"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index e0abbe1..718cba4 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -137,14 +137,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Pozivanje putem WiFi-ja | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Pozivanje putem WiFi-ja"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"WiFi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Pozivanje putem WIFi-ja"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Isključeno"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferira se WiFi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferira se mobilna mreža"</string>
@@ -536,6 +532,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Omogućava aplikaciji da čita lokacije iz vaše kolekcije medija."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> traži autentifikaciju."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomičan otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nije uspjela obrada otiska prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor za otisak prsta je prljav. Očistite ga i pokušajte ponovo."</string>
@@ -543,7 +544,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Prst je uklonjen presporo. Pokušajte ponovo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisak prsta je potvrđen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Lice je provjereno"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Lice je provjereno, pritisnite dugme za potvrdu"</string>
@@ -599,14 +599,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Omogućava aplikaciji izmjenu postavki sinhroniziranja za račun. Naprimjer, ovim se može omogućiti sinhroniziranje aplikacije People sa računom."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"čitanje statistike sinhroniziranja"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Omogućava aplikaciji čitanje statistike sinhroniziranja za račun, uključujući historiju događaja sinhroniziranja i količinu sinhroniziranih podataka."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"čitanje sadržaja USB pohrane"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"čitanje sadržaja SD kartice"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Omogućava aplikaciji čitanje sadržaja USB pohrane."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Omogućava aplikaciji čitanje sadržaja SD kartice."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"mijenjanje ili brisanje sadržaja USB pohrane"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"mijenjanje ili brisanje sadržaja SD kartice"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Dozvoljava pisanje na USB pohranu."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Dozvoljava aplikaciji pisanje na SD karticu."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"čita sadržaj vaše dijeljene pohrane"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Omogućava aplikaciji čitanje sadržaja vaše dijeljenje pohrane."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"mijenja ili briše sadržaj vaše dijeljene pohrane"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Omogućava aplikaciji da piše sadržaj vaše dijeljene pohrane."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"Uputi/primi SIP pozive"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Dozvoljava aplikaciji upućivanje i prijem SIP poziva."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registriraj nove telekom SMS veze"</string>
@@ -1125,6 +1121,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakažite događaj za odabrano vrijeme"</string>
<string name="view_flight" msgid="7691640491425680214">"Prati"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Pratite odabrani let"</string>
+ <string name="translate" msgid="9218619809342576858">"Prevedi"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevedi odabrani tekst"</string>
+ <string name="define" msgid="7394820043869954211">"Definirajte"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definiranje odabranog teksta"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke funkcije sistema možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1485,8 +1485,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> od <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Gotovo"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Brisanje USB pohrane..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Brisanje SD kartice..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Brisanje dijeljene pohrane…"</string>
<string name="share" msgid="1778686618230011964">"Podijelite"</string>
<string name="find" msgid="4808270900322985960">"Pronađi"</string>
<string name="websearch" msgid="4337157977400211589">"Internet pretraživanje"</string>
@@ -1868,8 +1867,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS zahtjev je promijenjen u USSD zahtjev"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Promijenjeno u novi SS zahtjev"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil za posao"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Upozoreni"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Proširi"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Suzi"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"aktiviraj/deaktiviraj proširenje"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index cb2c183..c68e524 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi‑Fi (<xliff:g id="SPN">%s</xliff:g>)"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Trucades per Wi‑Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi (<xliff:g id="SPN">%s</xliff:g>)"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Trucades per Wi‑Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi‑Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Trucades per Wi‑Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivat"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferència per la Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferència per dades mòbils"</string>
@@ -251,7 +247,7 @@
<string name="global_action_settings" msgid="1756531602592545966">"Configuració"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistència"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. per veu"</string>
- <string name="global_action_lockdown" msgid="1099326950891078929">"Bloqueja"</string>
+ <string name="global_action_lockdown" msgid="1099326950891078929">"Bloq. de seguretat"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"+999"</string>
<string name="notification_hidden_text" msgid="6351207030447943784">"Notificació nova"</string>
<string name="notification_channel_virtual_keyboard" msgid="6969925135507955575">"Teclat virtual"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permet que l\'aplicació llegeixi les ubicacions de les teves col·leccions multimèdia."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"L\'aplicació <xliff:g id="APP">%s</xliff:g> vol que t\'autentiquis."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Maquinari biomètric no disponible"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"No s\'ha reconegut"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor d\'empremtes digitals està brut. Neteja\'l i torna-ho a provar."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"No s\'ha reconegut"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"L\'empremta digital s\'ha autenticat"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Cara autenticada; prem el botó per confirmar"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permet que una aplicació modifiqui la configuració de sincronització d\'un compte. Per exemple, aquesta acció es pot fer servir per activar la sincronització de l\'aplicació Persones amb un compte."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"llegir les estadístiques de sincronització"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permet que una aplicació llegeixi les estadístiques de sincronització d\'un compte, inclòs l\'historial d\'esdeveniments sincronitzats i quantes dades se sincronitzen."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"consultar contingut emmagatzematge USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lectura del contingut de la targeta SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permet que l\'aplicació llegeixi el contingut de l\'emmagatzematge USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permet que l\'aplicació llegeixi el contingut de la targeta SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"editar o suprimir contingut d\'USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifica o suprimeix el contingut de la targeta SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permet que l\'aplicació escrigui a l\'emmagatzematge USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet a l\'aplicació escriure a la targeta SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"llegir cont. d\'emmagatzematge compartit"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"L\'app pot llegir contingut de l\'emmagatzematge compartit."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"editar o suprimir cont. d\'emmagatzematge compartit"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"L\'app pot editar contingut de l\'emmagatzematge compartit."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"Fer i rebre trucades de SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permet que l\'aplicació pugui fer i rebre trucades de SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrar connexions SIM de telecomunicacions noves"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programa un esdeveniment per a la data seleccionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Fes un seguiment"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Fes un seguiment del vol seleccionat"</string>
+ <string name="translate" msgid="9218619809342576858">"Tradueix"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Tradueix el text seleccionat"</string>
+ <string name="define" msgid="7394820043869954211">"Defineix"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Defineix el text seleccionat"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 partida</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Fet"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"S\'està esborrant l\'emmagatzematge USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"S\'està esborrant la targeta SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"S\'està esborrant l\'emmagatzematge compartit…"</string>
<string name="share" msgid="1778686618230011964">"Comparteix"</string>
<string name="find" msgid="4808270900322985960">"Cerca"</string>
<string name="websearch" msgid="4337157977400211589">"Cerca al web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"La sol·licitud SS s\'ha canviat per una sol·licitud USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"S\'ha canviat a una nova sol·licitud SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil professional"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"S\'ha enviat una alerta"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Desplega"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Replega"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"desplega o replega"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 019e7e0..4758c07 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g>: Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Volání přes Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g>: VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Volání přes Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Volání přes WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Vypnuto"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferována síť W-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferována mobilní data"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Umožňuje aplikaci číst místa z vaší sbírky médií."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikace <xliff:g id="APP">%s</xliff:g> potřebuje provést ověření."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrický hardware není k dispozici"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nerozpoznáno"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Byla zjištěna jen část otisku prstu. Zkuste to znovu."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otisků prstů je znečištěn. Vyčistěte jej a zkuste to znovu."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Pohyb prstem byl příliš pomalý. Zkuste to znovu."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nerozpoznáno"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisk byl ověřen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Obličej byl ověřen"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Obličej byl ověřen, stiskněte tlačítko pro potvrzení"</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Umožňuje aplikaci změnit nastavení synchronizace v účtu. Pomocí tohoto oprávnění lze například zapnout synchronizaci aplikace Lidé s účtem."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"čtení statistických údajů o synchronizaci"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Umožňuje aplikaci číst statistické informace o synchronizaci v účtu, včetně historie uskutečněných synchronizací a informací o množství synchronizovaných dat."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"čtení obsahu v úložišti USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"čtení obsahu na kartě SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Umožňuje aplikaci čtení obsahu USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Umožňuje aplikaci čtení obsahu karty SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"úprava nebo mazání obsahu v úložišti USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"úprava nebo mazání obsahu na kartě SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Umožňuje aplikaci zapisovat do úložiště USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikaci zapisovat na kartu SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"čtení obsahu sdíleného úložiště"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Umožňuje aplikaci čtení obsahu sdíleného úložiště."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"úprava nebo mazání obsahu sdíleného úložiště"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Umožňuje aplikaci zápis obsahu do sdíleného úložiště."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"uskutečňování/příjem volání SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Povolí aplikaci uskutečňovat a přijímat volání SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrování nových komunikačních připojení přes SIM kartu"</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Naplánovat událost na vybraný čas"</string>
<string name="view_flight" msgid="7691640491425680214">"Sledovat"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Sledovat vybraný let"</string>
+ <string name="translate" msgid="9218619809342576858">"Přeložit"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Přeložit vybraný text"</string>
+ <string name="define" msgid="7394820043869954211">"Definovat"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definuje vybraný text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1506,8 +1506,7 @@
<item quantity="one">1 shoda</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Hotovo"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Mazání úložiště USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Mazání karty SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Mazání sdíleného úložiště…"</string>
<string name="share" msgid="1778686618230011964">"Sdílet"</string>
<string name="find" msgid="4808270900322985960">"Najít"</string>
<string name="websearch" msgid="4337157977400211589">"Vyhledat na webu"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Požadavek SS byl změněn na požadavek USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Změněno na nový požadavek SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Pracovní profil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Upozorněno"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Rozbalit"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Sbalit"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"přepnout rozbalení"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index f7013ec..0167acf 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi via <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi-opkald | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi via <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi-opkald"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi-opkald"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Fra"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"WiFi-netværk er foretrukket"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobildata foretrækkes"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tillader, at appen kan læse placeringer fra din mediesamling."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g>-appen kræver din godkendelse."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk hardware er ikke tilgængelig"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke genkendt"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Der blev registreret et delvist fingeraftryk. Prøv igen."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensoren til registrering af fingeraftryk er beskidt. Tør den af, og prøv igen."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Du bevægede fingeren for langsomt. Prøv igen."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke genkendt"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeraftrykket blev godkendt"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Ansigtet er godkendt"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Ansigtet er godkendt. Tryk på Bekræft."</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Tillader, at en app kan ændre synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. anvendes til at aktivere synkronisering af appen Personer med en konto."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"læse synkroniseringsstatistikker"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Tillader, at en app kan læse synkroniseringsstatistikkerne for en konto, f.eks. historikken for synkroniserede begivenheder og hvor meget data der synkroniseres."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"læse USB-lagerets indhold"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"læse indholdet af dit SD-kort"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Tillader, at appen læser indholdet på dit USB-lager."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Tillader, at appen læser indholdet af dit SD-kort."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ændre eller slette indhold på USB-lager"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ændre eller slette indholdet på dit SD-kort"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Lader appen skrive til USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillader, at appen kan skrive til SD-kortet."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"læse indholdet af din delte lagerplads"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Tillader, at appen kan læse indholdet af din delte lagerplads."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ændre eller slette indholdet af din delte lagerplads"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Tillader, at appen kan skrive indholdet af din delte lagerplads."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"foretage/modtage SIP-opkald"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Tillader, at appen foretager og modtager SIP-opkald."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrere nye telefon-SIM-forbindelser"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planlæg begivenhed på det valgte tidspunkt"</string>
<string name="view_flight" msgid="7691640491425680214">"Spor"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Følg det valgte fly"</string>
+ <string name="translate" msgid="9218619809342576858">"Oversæt"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Oversæt den markerede tekst"</string>
+ <string name="define" msgid="7394820043869954211">"Definer"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definer den markerede tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> af <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Udfør"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Sletter USB-lageret..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Sletter SD-kortet..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Sletter delt lagerplads…"</string>
<string name="share" msgid="1778686618230011964">"Del"</string>
<string name="find" msgid="4808270900322985960">"Find"</string>
<string name="websearch" msgid="4337157977400211589">"Websøgning"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-anmodningen blev ændret til en USSD-anmodning"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Ændret til en SS-anmodning"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Arbejdsprofil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Underrettet"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Udvid"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Skjul"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"Slå udvidelse til eller fra"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2bad90e..a14a871 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> WLAN"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WLAN-Telefonie | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWLAN"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"WLAN-Telefonie"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"WLAN"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WLAN-Telefonie"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Aus"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"WLAN bevorzugt"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobilverbindung bevorzugt"</string>
@@ -533,14 +529,18 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ermöglicht der App, Standorte aus deiner Mediensammlung abzurufen."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> möchte, dass du dich authentifizierst."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrische Hardware nicht verfügbar"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nicht erkannt"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinige ihn und versuche es noch einmal."</string>
- <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt, bitte noch einmal versuchen"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger zu langsam bewegt. Bitte versuche es noch einmal."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nicht erkannt"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerabdruck wurde authentifiziert"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Gesicht authentifiziert"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Gesicht authentifiziert, bitte bestätigen"</string>
@@ -549,7 +549,7 @@
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche. Bitte versuche es später noch einmal."</string>
+ <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Zu viele Versuche. Der Fingerabdrucksensor wurde deaktiviert."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Keine Fingerabdrücke erfasst."</string>
@@ -581,7 +581,7 @@
<string name="face_error_no_space" msgid="8224993703466381314">"Gesicht kann nicht gespeichert werden."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Gesichtserkennung abgebrochen."</string>
<string name="face_error_user_canceled" msgid="8943921120862164539">"Gesichtsauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche. Versuch es später noch einmal."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zu viele Versuche. Gesichtserkennung deaktiviert."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Versuch es noch einmal."</string>
<string name="face_error_not_enrolled" msgid="9166792142679691323">"Kein Gesicht erfasst."</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Ermöglicht der App, die Synchronisierungseinstellungen eines Kontos zu ändern. Dies kann beispielsweise dazu verwendet werden, die Synchronisierung von Kontakten mit einem Konto zu aktivieren."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"Synchronisierungsstatistiken lesen"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Ermöglicht der App, die Synchronisierungsstatistiken eines Kontos zu lesen, einschließlich des Verlaufs von Synchronisierungsereignissen und der Menge synchronisierter Daten."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB-Speicherinhalte lesen"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD-Karteninhalte lesen"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Ermöglicht der App, den USB-Speicher zu lesen"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Ermöglicht der App, den Inhalt einer SD-Karte zu lesen"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB-Speicherinhalte ändern oder löschen"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD-Karteninhalte ändern oder löschen"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Ermöglicht der App, in den USB-Speicher zu schreiben"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ermöglicht der App, auf die SD-Karte zu schreiben"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"Inhalte deines freigegebenen Speichers lesen"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"So kann die App Inhalte deines freigegebenen Speichers lesen."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"Inhalte deines freigegebenen Speichers ändern oder löschen"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"So kann die App Inhalte deines freigegebenen Speichers erstellen."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP-Anrufe tätigen/empfangen"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Ermöglicht der App das Tätigen und Empfangen von SIP-Anrufen"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"Neue SIM-Telekommunikationsverbindungen registrieren"</string>
@@ -627,13 +623,13 @@
<string name="permlab_accessNotifications" msgid="7673416487873432268">"Auf Benachrichtigungen zugreifen"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ermöglicht der App das Abrufen, Überprüfen und Löschen von Benachrichtigungen, einschließlich Benachrichtigungen, die von anderen Apps gepostet wurden"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"An Benachrichtigungs-Listener-Dienst binden"</string>
- <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ermöglicht dem Eigentümer, sich an die Oberfläche der obersten Ebene eines Benachrichtigungs-Listener-Dienstes zu binden. Sollte nie für normale Apps benötigt werden."</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ermöglicht dem Inhaber, sich an die Oberfläche der obersten Ebene eines Benachrichtigungs-Listener-Dienstes zu binden. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"An einen Bedingungsproviderdienst binden"</string>
- <string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Ermöglicht dem Eigentümer, sich an die Oberfläche eines Bedingungsproviderdienstes auf oberster Ebene zu binden. Für normale Apps sollte dies nie erforderlich sein."</string>
+ <string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Ermöglicht dem Inhaber, sich an die Oberfläche eines Bedingungsproviderdienstes auf oberster Ebene zu binden. Für normale Apps sollte dies nie erforderlich sein."</string>
<string name="permlab_bindDreamService" msgid="4153646965978563462">"An Dream-Dienst binden"</string>
<string name="permdesc_bindDreamService" msgid="7325825272223347863">"Ermöglicht der App, sich an die Oberfläche eines Dream-Dienstes auf oberster Ebene zu binden. Für normale Apps sollte dies nie erforderlich sein."</string>
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"Vom Mobilfunkanbieter bereitgestellte Konfigurations-App aufrufen"</string>
- <string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Ermöglicht dem Eigentümer, die vom Mobilfunkanbieter bereitgestellte Konfigurations-App aufzurufen. Sollte für normale Apps nie benötigt werden."</string>
+ <string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Ermöglicht dem Inhaber, die vom Mobilfunkanbieter bereitgestellte Konfigurations-App aufzurufen. Sollte für normale Apps nie benötigt werden."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Informationen zu den Netzwerkbedingungen erfassen"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Ermöglicht der App, Informationen zu den Netzwerkbedingungen zu erfassen. Sollte für normale Apps nie benötigt werden."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"Kalibrierung für Eingabegerät ändern"</string>
@@ -645,9 +641,9 @@
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM-Zertifikate entfernen"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Ermöglicht einer App das Entfernen von DRM-Zertifikaten. Sollte für normale Apps nie benötigt werden."</string>
<string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"An einen Mobilfunkanbieter-Messaging-Dienst binden"</string>
- <string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Ermöglicht dem Eigentümer die Bindung an die Oberfläche eines Mobilfunkanbieter-Messaging-Dienstes auf oberster Ebene. Für normale Apps sollte dies nie erforderlich sein."</string>
+ <string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Ermöglicht dem Inhaber die Bindung an die Oberfläche eines Mobilfunkanbieter-Messaging-Dienstes auf oberster Ebene. Für normale Apps sollte dies nie erforderlich sein."</string>
<string name="permlab_bindCarrierServices" msgid="3233108656245526783">"An Mobilfunkanbieter-Dienste binden"</string>
- <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Ermöglicht dem Eigentümer die Bindung an Mobilfunkanbieter-Dienste. Für normale Apps sollte dies nicht erforderlich sein."</string>
+ <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Ermöglicht dem Inhaber die Bindung an Mobilfunkanbieter-Dienste. Für normale Apps sollte dies nicht erforderlich sein."</string>
<string name="permlab_access_notification_policy" msgid="4247510821662059671">"Auf \"Bitte nicht stören\" zugreifen"</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Ermöglicht der App Lese- und Schreibzugriff auf die \"Bitte nicht stören\"-Konfiguration"</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Passwortregeln festlegen"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Termin für die ausgewählte Zeit planen"</string>
<string name="view_flight" msgid="7691640491425680214">"Verfolgen"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Ausgewählten Flug verfolgen"</string>
+ <string name="translate" msgid="9218619809342576858">"Übersetzen"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Ausgewählten Text übersetzen"</string>
+ <string name="define" msgid="7394820043869954211">"Definieren"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Ausgewählten Text definieren"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren eventuell nicht."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 Treffer</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Fertig"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB-Speicher wird gelöscht..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD-Karteninhalt wird gelöscht..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Freigegebener Speicher wird gelöscht…"</string>
<string name="share" msgid="1778686618230011964">"Teilen"</string>
<string name="find" msgid="4808270900322985960">"Suchen"</string>
<string name="websearch" msgid="4337157977400211589">"Websuche"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-Anfrage wurde in USSD-Anfrage geändert"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"In neue SS-Anfrage geändert"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Arbeitsprofil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Gewarnt"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Maximieren"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Minimieren"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"Maximierung ein-/auschalten"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 15e4b1c..3c251e2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Κλήση Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Κλήση Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Κλήση Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Ανενεργό"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Προτίμηση Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Προτίμηση δικτύου κινητής τηλεφωνίας"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Επιτρέπει στην εφαρμογή να διαβάσει τοποθεσίες από τη συλλογή πολυμέσων σας."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί έλεγχο ταυτότητας."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Δεν υπάρχει διαθέσιμος βιομετρικός εξοπλισμός"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Δεν αναγνωρίστηκε"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μερικό μοναδικό χαρακτηριστικό. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Ο αισθητήρας μοναδικού χαρακτηριστικού δεν είναι καθαρός. Καθαρίστε τον και δοκιμάστε ξανά."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Πολύ αργή κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Δεν αναγνωρίστηκε"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Έγινε έλεγχος ταυτότητας προσώπου"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Επιτρέπει σε μια εφαρμογή την τροποποίηση των ρυθμίσεων συγχρονισμού για έναν λογαριασμό. Για παράδειγμα, αυτό μπορεί να χρησιμοποιηθεί για να ενεργοποιηθεί ο συγχρονισμός της εφαρμογής \"Άτομα\" με έναν λογαριασμό."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"διαβάζει στατιστικά στοιχεία συγχρονισμού"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Επιτρέπει σε μια εφαρμογή την ανάγνωση των στατιστικών στοιχείων συγχρονισμού για έναν λογαριασμό, συμπεριλαμβανομένων του ιστορικού των συμβάντων συγχρονισμού και του όγκου των δεδομένων που συγχρονίζονται."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ανάγν. περιεχ. αποθηκ. χώρ.USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"διαβάζει το περιεχομένο της κάρτας SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Επιτρέπει στην εφαρμογή την ανάγνωση του περιεχομένου του αποθηκευτικού σας χώρου USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Επιτρέπει στην εφαρμογή την ανάγνωση του περιεχομένου της κάρτας SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"τροποποιεί/διαγράφει το USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"τροποποιεί ή διαγράφει τα περιεχόμενα της κάρτας SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Επιτρέπει στην εφαρμογή την εγγραφή στον αποθηκευτικό χώρο USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Επιτρέπει στην εφαρμογή την εγγραφή στην κάρτα SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ανάγνωση του περιεχομένου του κοινόχρηστου αποθηκευτικού χώρου σας"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Επιτρέπει στην εφαρμογή την ανάγνωση του περιεχομένου του κοινόχρηστου αποθηκευτικού χώρου σας."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"τροποποιεί ή διαγράφει το περιεχόμενο του κοινόχρηστου αποθηκευτικού χώρου σας"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Επιτρέπει στην εφαρμογή την εγγραφή του περιεχομένου του κοινόχρηστου αποθηκευτικού χώρου σας."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"πραγματοποιεί/λαμβάνει κλήσεις SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Επιτρέπει στην εφαρμογή να πραγματοποιεί και να λαμβάνει κλήσεις SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"πραγματοποιεί εγγραφή νέων συνδέσεων SIM τηλεπικοινωνιών"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Προγραμματισμός συμβάντος για επιλεγμένο χρόνο"</string>
<string name="view_flight" msgid="7691640491425680214">"Κομμάτι"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Παρακολούθηση επιλεγμένης πτήσης"</string>
+ <string name="translate" msgid="9218619809342576858">"Μετάφραση"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Μετάφραση επιλεγμένου κειμένου"</string>
+ <string name="define" msgid="7394820043869954211">"Ορισμός"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Ορισμός επιλεγμένου κειμένου"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο αποθηκευτικός χώρος εξαντλείται"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 αντιστοιχία</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Τέλος"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Διαγραφή αποθηκευτικού χώρου USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Διαγραφή κάρτας SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Διαγραφή κοινόχρηστου αποθηκευτικού χώρου…"</string>
<string name="share" msgid="1778686618230011964">"Κοινή χρ."</string>
<string name="find" msgid="4808270900322985960">"Εύρεση"</string>
<string name="websearch" msgid="4337157977400211589">"Αναζήτηση ιστού"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Το αίτημα SS τροποποιήθηκε σε αίτημα USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Τροποποιήθηκε σε νέο αίτημα SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Προφίλ εργασίας"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Ειδοποιήθηκε"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Ανάπτυξη"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Σύμπτυξη"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"εναλλαγή επέκτασης"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 8b6b404..4b983c0 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi Calling | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Off"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi preferred"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobile preferred"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Face authenticated. Please press confirm"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Allows an app to modify the sync settings for an account. For example, this can be used to enable syncing of the People app with an account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"read sync statistics"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"read the contents of your USB storage"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"read the contents of your SD card"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Allows the app to read the contents of your USB storage."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Allows the app to read the contents of your SD card."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Allows the app to write to the USB storage."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"read the contents of your shared storage"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Allows the app to read the contents of your shared storage."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modify or delete the contents of your shared storage"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"make/receive SIP calls"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Allows the app to make and receive SIP calls."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"register new telecom SIM connections"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Schedule event for selected time"</string>
<string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Track selected flight"</string>
+ <string name="translate" msgid="9218619809342576858">"Translate"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Translate selected text"</string>
+ <string name="define" msgid="7394820043869954211">"Define"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Define selected text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 match</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Done"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Erasing USB storage..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Erasing SD card…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Erasing shared storage…"</string>
<string name="share" msgid="1778686618230011964">"Share"</string>
<string name="find" msgid="4808270900322985960">"Find"</string>
<string name="websearch" msgid="4337157977400211589">"Web Search"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Changed to new SS request"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Work profile"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expand"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Collapse"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"toggle expansion"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 4929e87..821ac1e 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi Calling | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Off"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi preferred"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobile preferred"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Face authenticated. Please press confirm"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Allows an app to modify the sync settings for an account. For example, this can be used to enable syncing of the People app with an account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"read sync statistics"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"read the contents of your USB storage"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"read the contents of your SD card"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Allows the app to read the contents of your USB storage."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Allows the app to read the contents of your SD card."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Allows the app to write to the USB storage."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"read the contents of your shared storage"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Allows the app to read the contents of your shared storage."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modify or delete the contents of your shared storage"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"make/receive SIP calls"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Allows the app to make and receive SIP calls."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"register new telecom SIM connections"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Schedule event for selected time"</string>
<string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Track selected flight"</string>
+ <string name="translate" msgid="9218619809342576858">"Translate"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Translate selected text"</string>
+ <string name="define" msgid="7394820043869954211">"Define"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Define selected text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 match</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Done"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Erasing USB storage..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Erasing SD card…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Erasing shared storage…"</string>
<string name="share" msgid="1778686618230011964">"Share"</string>
<string name="find" msgid="4808270900322985960">"Find"</string>
<string name="websearch" msgid="4337157977400211589">"Web Search"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Changed to new SS request"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Work profile"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expand"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Collapse"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"toggle expansion"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 8b6b404..4b983c0 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi Calling | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Off"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi preferred"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobile preferred"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Face authenticated. Please press confirm"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Allows an app to modify the sync settings for an account. For example, this can be used to enable syncing of the People app with an account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"read sync statistics"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"read the contents of your USB storage"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"read the contents of your SD card"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Allows the app to read the contents of your USB storage."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Allows the app to read the contents of your SD card."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Allows the app to write to the USB storage."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"read the contents of your shared storage"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Allows the app to read the contents of your shared storage."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modify or delete the contents of your shared storage"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"make/receive SIP calls"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Allows the app to make and receive SIP calls."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"register new telecom SIM connections"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Schedule event for selected time"</string>
<string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Track selected flight"</string>
+ <string name="translate" msgid="9218619809342576858">"Translate"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Translate selected text"</string>
+ <string name="define" msgid="7394820043869954211">"Define"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Define selected text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 match</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Done"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Erasing USB storage..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Erasing SD card…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Erasing shared storage…"</string>
<string name="share" msgid="1778686618230011964">"Share"</string>
<string name="find" msgid="4808270900322985960">"Find"</string>
<string name="websearch" msgid="4337157977400211589">"Web Search"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Changed to new SS request"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Work profile"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expand"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Collapse"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"toggle expansion"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 8b6b404..4b983c0 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi Calling | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Off"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi preferred"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobile preferred"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Face authenticated. Please press confirm"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Allows an app to modify the sync settings for an account. For example, this can be used to enable syncing of the People app with an account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"read sync statistics"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"read the contents of your USB storage"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"read the contents of your SD card"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Allows the app to read the contents of your USB storage."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Allows the app to read the contents of your SD card."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Allows the app to write to the USB storage."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"read the contents of your shared storage"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Allows the app to read the contents of your shared storage."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modify or delete the contents of your shared storage"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"make/receive SIP calls"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Allows the app to make and receive SIP calls."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"register new telecom SIM connections"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Schedule event for selected time"</string>
<string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Track selected flight"</string>
+ <string name="translate" msgid="9218619809342576858">"Translate"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Translate selected text"</string>
+ <string name="define" msgid="7394820043869954211">"Define"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Define selected text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 match</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Done"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Erasing USB storage..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Erasing SD card…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Erasing shared storage…"</string>
<string name="share" msgid="1778686618230011964">"Share"</string>
<string name="find" msgid="4808270900322985960">"Find"</string>
<string name="websearch" msgid="4337157977400211589">"Web Search"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Changed to new SS request"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Work profile"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expand"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Collapse"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"toggle expansion"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index b646d86b..3781945 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -529,6 +529,9 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
+ <string name="biometric_error_user_canceled" msgid="2260175018114348727">"Authentication canceled"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognized"</string>
+ <string name="biometric_error_canceled" msgid="349665227864885880">"Authentication canceled"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -536,7 +539,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognized"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Face authenticated, please press confirm"</string>
@@ -592,14 +594,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Allows an app to modify the sync settings for an account. For example, this can be used to enable sync of the People app with an account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"read sync statistics"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"read the contents of your USB storage"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"read the contents of your SD card"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Allows the app to read the contents of your USB storage."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Allows the app to read the contents of your SD card."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Allows the app to write to the USB storage."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"read the contents of your shared storage"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Allows the app to read the contents of your shared storage."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modify or delete the contents of your shared storage"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"make/receive SIP calls"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Allows the app to make and receive SIP calls."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"register new telecom SIM connections"</string>
@@ -1101,6 +1099,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Schedule event for selected time"</string>
<string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Track selected flight"</string>
+ <string name="translate" msgid="9218619809342576858">"Translate"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Translate selected text"</string>
+ <string name="define" msgid="7394820043869954211">"Define"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Define selected text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure you have 250MB of free space and restart."</string>
@@ -1456,8 +1458,7 @@
<item quantity="one">1 match</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Done"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Erasing USB storage…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Erasing SD card…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Erasing shared storage…"</string>
<string name="share" msgid="1778686618230011964">"Share"</string>
<string name="find" msgid="4808270900322985960">"Find"</string>
<string name="websearch" msgid="4337157977400211589">"Web Search"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index c51e1aa..50ebc49 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Llamada por Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi de <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Llamada por Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Llamada por Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivada"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Red Wi-Fi preferida"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Red móvil preferida"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que la app lea las ubicaciones de tu colección de contenido multimedia."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"La aplicación de <xliff:g id="APP">%s</xliff:g> quiere autenticarte"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"No hay hardware biométrico disponible"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"No se reconoció"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"La huella digital se detectó parcialmente. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se pudo procesar la huella digital. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Limpia el sensor y vuelve a intentarlo."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Moviste el dedo muy lento. Vuelve a intentarlo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"No se reconoció"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Se autenticó la huella digital"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Se autenticó el rostro"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Se autenticó el rostro; presiona Confirmar"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permite que la aplicación modifique la configuración de sincronización de una cuenta. Este permiso se puede utilizar, por ejemplo, para activar la sincronización de la aplicación Personas con una cuenta."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"leer estadística de sincronización"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permite que la aplicación consulte las estadísticas de sincronización de una cuenta, por ejemplo, el historial de eventos sincronizados y la cantidad de datos sincronizados."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"leer contenido de disp. USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"leer el contenido de tu tarjeta SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permite leer contenido de USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite que la aplicación lea el contenido de la tarjeta SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar/borrar contenido USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar o eliminar el contenido de la tarjeta SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite que la aplicación escriba en el almacenamiento USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Admite que la aplicación escriba en la tarjeta SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ver almacenamiento compartido"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Ver almacenamiento compartido"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"Cambia/borra almac. compartido"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Editar almacen. compartido"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"realizar/recibir llamadas SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permite que la aplicación realice y reciba llamadas SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"Registrar nuevas conexiones SIM de telecomunicaciones"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programar un evento para la hora seleccionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Realizar seguimiento"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seguir el vuelo seleccionado"</string>
+ <string name="translate" msgid="9218619809342576858">"Traducir"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traducir texto seleccionado"</string>
+ <string name="define" msgid="7394820043869954211">"Definir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definir texto seleccionado"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 coincidencia</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Listo"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Borrando almacenamiento USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Borrando tarjeta SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Borrando almacenamiento compartido…"</string>
<string name="share" msgid="1778686618230011964">"Compartir"</string>
<string name="find" msgid="4808270900322985960">"Buscar"</string>
<string name="websearch" msgid="4337157977400211589">"Buscar en la Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Se cambió la solicitud SS por una solicitud USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Se cambió a una nueva solicitud SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabajo"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alerta enviada"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expandir"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Contraer"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"activar o desactivar la expansión"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a7ec220..7a992b4 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi‑Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Llamada por Wi‑Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWiFi de <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Llamada por Wi‑Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi‑Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Llamada por Wi‑Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWiFi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivado"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Dar preferencia a Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferir datos móviles"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que la aplicación lea las ubicaciones de tu colección de contenido multimedia."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> solicita tu autenticación."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico no disponible"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"No se reconoce"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Se ha detectado una huella digital parcial. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Límpialo y vuelve a intentarlo."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Has movido el dedo demasiado despacio. Vuelve a intentarlo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"No se reconoce"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Se ha autenticado la huella digital"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Se ha autenticado la cara, pulsa para confirmar"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permite que la aplicación modifique la configuración de sincronización de una cuenta. Este permiso se puede utilizar, por ejemplo, para habilitar la sincronización de la aplicación Contactos con una cuenta."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"leer estadísticas de sincronización"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permite que la aplicación consulte las estadísticas de sincronización de una cuenta (por ejemplo, el historial de eventos sincronizados y la cantidad de datos sincronizados)."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"consultar el contenido del almacenamiento USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"consultar el contenido de la tarjeta SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permite que la aplicación lea el contenido del almacenamiento USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite que la aplicación lea el contenido de la tarjeta SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"editar o borrar contenido de almacenamiento USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar o eliminar el contenido de la tarjeta SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite escribir en el almacenamiento USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que la aplicación escriba en la tarjeta SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"leer cont. de almacenamiento compartido"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permite que app lea contenido de almacenamiento compartido."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"editar/eliminar contenido de almacenamiento compartido"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Permite que app edite contenido de almacenamiento compartido."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"hacer/recibir llamadas SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permite que la aplicación haga y reciba llamadas SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registras conexiones de SIM de telecomunicaciones nuevas"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programar un evento para la hora seleccionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Buscar"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seguir el vuelo seleccionado"</string>
+ <string name="translate" msgid="9218619809342576858">"Traducir"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traducir el texto seleccionado"</string>
+ <string name="define" msgid="7394820043869954211">"Definir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definir el texto seleccionado"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 coincidencia</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Listo"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Borrando almacenamiento USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Borrando tarjeta SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Borrando almacenamiento compartido…"</string>
<string name="share" msgid="1778686618230011964">"Compartir"</string>
<string name="find" msgid="4808270900322985960">"Buscar"</string>
<string name="websearch" msgid="4337157977400211589">"Búsqueda web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Se ha cambiado la solicitud de SS a una solicitud de USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Se ha cambiado a una nueva solicitud de SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabajo"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Con sonido"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Mostrar"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Ocultar"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"alternar mostrar y ocultar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 05486c2..e633ab4 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g>: WiFi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi-kõne | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g>: VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"WiFi-kõned"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"WiFi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WiFi-kõned"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Väljas"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"WiFi eelistusega"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Eelistatud on mobiilne andmeside"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Võimaldab rakendusel lugeda teie meediakogus olevaid asukohti."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Rakendus <xliff:g id="APP">%s</xliff:g> soovib autentimist."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biomeetriline riistvara ei ole saadaval"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ei tuvastatud"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Tuvastati osaline sõrmejälg. Proovige uuesti."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sõrmejäljeandur on must. Puhastage see ja proovige uuesti."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Sõrm liikus liiga aeglaselt. Proovige uuesti."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Ei tuvastatud"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sõrmejälg autenditi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Nägu on autenditud"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Nägu on autenditud, vajutage käsku Kinnita"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Võimaldab rakendusel muuta konto sünkroonimisseadeid. Näiteks saab seda kasutada, et lubada rakenduse Inimesed sünkroonimine kontoga."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"loe sünkroonimisstatistikat"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Võimaldab rakendusel lugeda konto sünkroonimisstatistikat, sh sünkroonimissündmuste ajalugu ja sünkroonitud andmete hulka."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB-salvestusruumi sisu lugemine"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD-kaardi sisu lugemine"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Lubab rakendusel lugeda USB-salvestusruumi sisu."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Lubab rakendusel lugeda SD-kaardi sisu."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"muutke, kustut. USB-ruumi sisu"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD-kaardi sisu muutmine või kustutamine"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Võimaldab rakendusel kirjutada USB-mäluseadmele."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Võimaldab rakendusel kirjutada SD-kaardile."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"Jagatud salvestusruumi sisu lugemine"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Lubab rakendusel lugeda jagatud salvestusruumi sisu."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"Jagatud salvestusruumi sisu muutmine või kustutamine"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Lubab rakendusel kirjutada jagatud salvestusruumi sisu."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP-kõnede tegemine/vastuvõtmine"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Lubab rakendusel teha ja vastu võtta SIP-kõnesid."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"uute telekommunikatsiooni SIM-kaardi ühenduste registreerimine"</string>
@@ -1088,23 +1084,27 @@
<string name="inputMethod" msgid="1653630062304567879">"Sisestusmeetod"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoimingud"</string>
<string name="email" msgid="4560673117055050403">"E-post"</string>
- <string name="email_desc" msgid="3638665569546416795">"Valitud aadressile meili saatmine"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Saada valitud aadressile meil"</string>
<string name="dial" msgid="1253998302767701559">"Helista"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Valitud telefoninumbrile helistamine"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Helista valitud telefoninumbrile"</string>
<string name="map" msgid="5441053548030107189">"Kaart"</string>
- <string name="map_desc" msgid="1836995341943772348">"Valitud aadressi leidmine"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Leia valitud aadress"</string>
<string name="browse" msgid="1245903488306147205">"Ava"</string>
- <string name="browse_desc" msgid="8220976549618935044">"Valitud URL-i avamine"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"Ava valitud URL"</string>
<string name="sms" msgid="4560537514610063430">"Saada sõnum"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Valitud telefoninumbrile sõnumi saatmine"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Saada valitud telefoninumbrile sõnum"</string>
<string name="add_contact" msgid="7867066569670597203">"Lisa"</string>
- <string name="add_contact_desc" msgid="4830217847004590345">"Kontaktide hulka lisamine"</string>
+ <string name="add_contact_desc" msgid="4830217847004590345">"Lisa kontaktide hulka"</string>
<string name="view_calendar" msgid="979609872939597838">"Kuva"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Valitud aja vaatamine kalendris"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Kuva valitud aeg kalendris"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Lisa ajakavasse"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Ürituse ajastamine valitud ajale"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Ajasta üritus valitud ajale"</string>
<string name="view_flight" msgid="7691640491425680214">"Jälgi"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Valitud lennu jälgimine"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Jälgi valitud lendu"</string>
+ <string name="translate" msgid="9218619809342576858">"Tõlgi"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Tõlgi valitud tekst"</string>
+ <string name="define" msgid="7394820043869954211">"Defineeri"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Defineeri valitud tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 vaste</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Valmis"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB-salvestusruumi kustutamine ..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD-kaardi kustutamine ..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Jagatud salvestusruumi tühjendamine …"</string>
<string name="share" msgid="1778686618230011964">"Jaga"</string>
<string name="find" msgid="4808270900322985960">"Otsi"</string>
<string name="websearch" msgid="4337157977400211589">"Veebiotsing"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-taotlus muudeti USSD-taotluseks"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Muudeti uueks SS-taotluseks"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Tööprofiil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Teavitatud"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Laienda"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Ahenda"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"vaheta laiendamist"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index b301568b..ea9414f 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi bidezko deiak | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi bidezko deiak"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi sarea"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi bidezko deiak"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desaktibatuta"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi sarea hobesten da"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Datu-konexioa hobesten da"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Multimedia-edukien bildumako kokapena irakurtzea baimentzen die aplikazioei."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> aplikazioak autentifikatu egin nahi du."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrikoa ez dago erabilgarri"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ez da ezagutu"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hatz-marka digitala ez da osorik hauteman. Saiatu berriro."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ezin izan da prozesatu hatz-marka. Saiatu berriro."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Hatz-marka digitalen sentsorea zikina dago. Garbi ezazu, eta saiatu berriro."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Mantsoegi mugitu duzu hatza. Saiatu berriro."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Ez da ezagutu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autentifikatu da hatz-marka"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Autentifikatu da aurpegia"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Autentifikatu da aurpegia; sakatu Berretsi"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Kontu baten sinkronizazio-ezarpenak aldatzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa kontu batekin sinkronizatzeko erabil daiteke."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"irakurri sinkronizazio-estatistikak"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Kontu baten sinkronizazio-estatistikak irakurtzeko baimena ematen dio; besteak beste, sinkronizazio-gertaeren historia eta sinkronizatutako datu kopurua."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB memoriako edukia irakurtzea"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"irakurri SD txarteleko edukia"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"USB memoriako edukia irakurtzeko aukera ematen die aplikazioei."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"SD txartelaren edukia irakurtzeko aukera ematen die aplikazioei."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB memoriako edukia aldatzea edo ezabatzea"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"aldatu edo ezabatu SD txarteleko edukia"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB memorian idaztea baimentzen die aplikazioei."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SD txartelean idaztea baimentzen die aplikazioei."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"Irakurri biltegi partekatuko edukia"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Biltegi partekatuko edukia irakurtzea baimentzen die aplikazioei."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"Aldatu edo ezabatu biltegi partekatuko edukia"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Biltegi partekatuko edukian idaztea baimentzen die aplikazioei."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"egin/jaso SIP deiak"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP deiak egitea eta jasotzea baimentzen die aplikazioei."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"erregistratu telekomunikabideekiko SIM konexio berriak"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Antolatu gertaera bat hautatutako ordurako"</string>
<string name="view_flight" msgid="7691640491425680214">"Egin jarraipena"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Egin hautatutako hegaldiaren jarraipena"</string>
+ <string name="translate" msgid="9218619809342576858">"Itzuli"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Itzuli hautatutako testua"</string>
+ <string name="define" msgid="7394820043869954211">"Definitu"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definitu hautatutako testua"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memoria betetzen ari da"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">Emaitza bat</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Eginda"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB memoria ezabatzen…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD txartela ezabatzen…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Biltegiratze partekatuko eduki guztia ezabatzen…"</string>
<string name="share" msgid="1778686618230011964">"Partekatu"</string>
<string name="find" msgid="4808270900322985960">"Aurkitu"</string>
<string name="websearch" msgid="4337157977400211589">"Web-bilaketa"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS eskaera USSD eskaerara aldatu da"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"SS eskaera berrira aldatu da"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Work profila"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Egin du soinua"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Zabaldu"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Tolestu"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"zabaldu edo tolestu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 00a86ce..1099169 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"درحال تماس ازطریق WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"تماس ازطریق Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"تماس ازطریق WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"خاموش"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi ترجیحی"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"داده شبکه تلفن همراه ارجح است"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"به برنامه اجازه میدهد مکانها را از مجموعه رسانهتان بخواند."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"برنامه <xliff:g id="APP">%s</xliff:g> میخواهد احراز هویت کند."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"سختافزار زیستسنجی دردسترس نیست"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"شناسایی نشد"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"حرکت انگشت خیلی آهسته بود. لطفاً دوباره امتحان کنید."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"شناسایی نشد"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"اثر انگشت احراز هویت شد"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"چهره احراز هویت شد"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"چهره احراز هویت شد، لطفاً تأیید را فشار دهید"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"به برنامه اجازه میدهد تنظیمات همگامسازی را برای حساب تغییر دهد. بهعنوان مثال، از این ویژگی میتوان برای فعال کردن همگامسازی برنامه «افراد» با یک حساب استفاده کرد."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"خواندن اطلاعات آماری همگامسازی"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"به یک برنامه اجازه میدهد وضعیت همگامسازی یک حساب را بخواند، از جمله سابقه رویدادهای همگامسازی و میزان دادههای همگامسازی شده."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"خواندن محتویات حافظهٔ USB شما"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"خواندن محتویات کارت SD شما"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"به برنامه اجازه میدهد محتواهای فضای ذخیره USB را بخواند."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"به برنامه اجازه میدهد محتواهای کارت SD شما را بخواند."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"تغییر یا حذف محتویات حافظه USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"تغییر یا حذف محتوای کارت SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"به برنامه اجازه میدهد تا در حافظهٔ USB بنویسد."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"به برنامه اجازه میدهد تا در کارت SD بنویسد."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"خواندن محتوای فضای ذخیره همرسانیشده"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"به برنامه اجازه میدهد محتوای فضای ذخیره همرسانیشدهتان را بخواند."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"تغییر یا حذف محتوای فضای ذخیره همرسانیشده"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"به برنامه اجازه میدهد محتوای فضای ذخیره همرسانیشدهتان را بنویسد."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"تماس گرفتن/دریافت تماس از طریق SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"به برنامه اجازه میدهد تماسهای SIP بگیرد یا دریافت کند."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ثبت ارتباطات سیم کارت مخابراتی جدید"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"زمانبندی رویداد برای زمان انتخابی"</string>
<string name="view_flight" msgid="7691640491425680214">"پیگیری"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ردیابی پرواز انتخابی"</string>
+ <string name="translate" msgid="9218619809342576858">"ترجمه کردن"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ترجمه کردن نوشتار انتخابشده"</string>
+ <string name="define" msgid="7394820043869954211">"تعریف"</string>
+ <string name="define_desc" msgid="7910883642444919726">"تعریف نوشتار انتخابی"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"حافظه درحال پر شدن است"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"فضای ذخیرهسازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راهاندازی مجدد کنید."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> از <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"تمام"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"در حال پاک کردن حافظهٔ USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"در حال پاک کردن کارت SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"درحال پاک کردن فضای ذخیرهسازی همرسانیشده…"</string>
<string name="share" msgid="1778686618230011964">"اشتراکگذاری"</string>
<string name="find" msgid="4808270900322985960">"یافتن"</string>
<string name="websearch" msgid="4337157977400211589">"جستجوی وب"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"درخواست SS به درخواست USSD تغییر کرد"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"به درخواست SS جدید تغییر کرد"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"نمایه کاری"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"هشدار ارسال شد"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"بزرگ کردن"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"کوچک کردن"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"روشن/خاموش کردن بزرگنمایی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 7fc02df..3f8f0d1 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi-puhelut | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi-puhelut"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi-puhelut"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Ei käytössä"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi ensisijainen"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobiiliverkko ensisijainen"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Antaa sovelluksen lukea mediakokoelmasi sijainteja."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> pyytää todentamista"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrinen laitteisto ei käytettävissä"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ei tunnistettu"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sormenjälki havaittiin vain osittain. Yritä uudelleen."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sormenjäljen käsittely epäonnistui. Yritä uudelleen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sormenjälkitunnistin on likainen. Puhdista tunnistin ja yritä uudelleen."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Liikutit sormea liian hitaasti. Yritä uudelleen."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Ei tunnistettu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sormenjälki tunnistettu"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Kasvot tunnistettu"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Kasvot tunnistettu, valitse Vahvista"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Antaa sovelluksen muokata tilin synkronointiasetuksia. Sovellus voi esimerkiksi ottaa Ihmiset-sovelluksen synkronoinnin käyttöön tilissä."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lue synkronointitilastoja"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Antaa sovelluksen lukea tilien synkronoinnin tilan sekä synkronoitujen tapahtumien historian ja kuinka paljon tietoja on synkronoitu."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"lue USB-tallennustilan sis."</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lue SD-kortin sisältöä"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Antaa sovelluksen lukea USB-tallennuslaitteen sisällön."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Antaa sovelluksen lukea SD-kortin sisällön."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"muokkaa tai poista USB:n sis."</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"muokkaa tai poista SD-kortin sisältöä"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Antaa sovelluksen kirjoittaa USB-tallennustilaan."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Antaa sovelluksen kirjoittaa SD-kortille."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"lukea jaetun tallennustilan sisällön"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Antaa sovelluksen lukea jaetun tallennustilan sisällön."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"muokata tai poistaa jaetun tallennustilan sisältöä"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Antaa sovelluksen kirjoittaa jaetun tallennustilan sisällön."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"soita/vastaanota SIP-puheluja"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Sallii sovelluksen soittaa ja vastaanottaa SIP-puheluja."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"rekisteröidä uusia tietoliikenne-SIM-yhteyksiä"</string>
@@ -1101,10 +1097,14 @@
<string name="add_contact_desc" msgid="4830217847004590345">"Lisää yhteystietoihin"</string>
<string name="view_calendar" msgid="979609872939597838">"Näytä"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Näytä valittu aika kalenterissa"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Aikataulu"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Aikatauluta"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ajoita tapahtuma valitulle ajalle"</string>
<string name="view_flight" msgid="7691640491425680214">"Seuraa"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seuraa valittua lentoa"</string>
+ <string name="translate" msgid="9218619809342576858">"Käännä"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Käännä valittu teksti."</string>
+ <string name="define" msgid="7394820043869954211">"Määrittele"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Määrittele valittu teksti"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 tulos</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Valmis"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Tyhjennetään USB-tallennustilaa..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Tyhjennetään SD-korttia..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Tyhjennetään jaettua tallennustilaa…"</string>
<string name="share" msgid="1778686618230011964">"Jaa"</string>
<string name="find" msgid="4808270900322985960">"Etsi"</string>
<string name="websearch" msgid="4337157977400211589">"Verkkohaku"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-pyyntö vaihdettu USSD-pyynnöksi"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Vaihdettu uudeksi SS-pyynnöksi"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Työprofiili"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Hälytti"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Laajenna"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Tiivistä"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"Laajenna/tiivistä painikkeella"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index b58a051..2bac413 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Appels Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"Voix par Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Appels Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Appels Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"Voix par Wi-Fi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Désactivé"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Réseau Wi-Fi de préférence"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Connexion cellulaire de préférence"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Autorise l\'application à lire les positions indiquées dans votre collection multimédia."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> vous demande de vous authentifier."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Matériel biométrique indisponible"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Données biométriques non reconnues"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partielle détectée. Veuillez essayer de nouveau."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de traiter les empreintes digitales. Veuillez essayer de nouveau."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes digitales est sale. Veuillez le nettoyer et essayer de nouveau."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Données biométriques non reconnues"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permet à une application de modifier les paramètres de synchronisation d\'un compte. Cette autorisation peut, par exemple, être utilisée pour activer la synchronisation de l\'application Contacts avec un compte."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lire les statistiques de synchronisation"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permet à une application d\'accéder aux statistiques de synchronisation d\'un compte, y compris l\'historique des événements de synchronisation et le volume de données synchronisées."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"lire le contenu du stockage USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lire le contenu de la carte SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permet à l\'application de lire le contenu de votre périphérique de stockage USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permet à l\'application de lire le contenu de votre carte SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifier ou supprimer le contenu de la mémoire USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifier ou supprimer le contenu de la carte SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permet à l\'application de modifier le contenu de la mémoire de stockage USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet à l\'application de modifier le contenu de la carte SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"lire le contenu de votre espace de stockage partagé"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permet à l\'application de lire le contenu de votre espace de stockage partagé."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modifier ou supprimer le contenu de votre espace de stockage partagé"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Autorise l\'application à écrire le contenu de votre espace de stockage partagé."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"faire et recevoir des appels SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Autorise l\'application à effectuer et à recevoir des appels SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"enregistrer de nouvelles connexions de télécommunication à l\'aide de la carte SIM"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifier un événement pour l\'heure sélectionnée"</string>
<string name="view_flight" msgid="7691640491425680214">"Effectuer le suivi"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Faire le suivi du vol sélectionné"</string>
+ <string name="translate" msgid="9218619809342576858">"Traduire"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduire le texte sélectionné"</string>
+ <string name="define" msgid="7394820043869954211">"Définir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Définir le texte sélectionné"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Terminé"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Effacement de la mémoire de stockage USB en cours…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Effacement de la carte SD en cours…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Effacement du stockage partagé en cours…"</string>
<string name="share" msgid="1778686618230011964">"Partager"</string>
<string name="find" msgid="4808270900322985960">"Trouver"</string>
<string name="websearch" msgid="4337157977400211589">"Recherche Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"La demande SS a été remplacée par une demande USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"La demande a été remplacée par une nouvelle demande SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil professionnel"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alerté"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Développer"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Réduire"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"activer/désactiver le développement"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6f9e0bc..c0c2ed8 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Appels Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWiFi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Appels Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Appels Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWiFi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Désactivé"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi de préférence"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Données mobiles de préférence"</string>
@@ -295,7 +291,7 @@
<string name="permgroupdesc_sms" msgid="4656988620100940350">"envoyer et consulter des SMS"</string>
<string name="permgrouprequest_sms" msgid="7168124215838204719">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'envoyer et d\'afficher des SMS ?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Stockage"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"accéder à des photos, à des contenus multimédias et à des fichiers sur votre appareil"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"accéder aux photos, contenus multimédias et fichiers sur votre appareil"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux photos, contenus multimédias et fichiers sur votre appareil ?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"enregistrer des fichiers audio"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Autorise l\'application à consulter des positions issues de votre bibliothèque multimédia."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"L\'application <xliff:g id="APP">%s</xliff:g> veut vous authentifier."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Matériel biométrique indisponible"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Non reconnu"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le lecteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Non reconnu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permet à une application de modifier les paramètres de synchronisation d\'un compte. Cette autorisation peut, par exemple, être utilisée pour activer la synchronisation de l\'application Contacts avec un compte."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"Lecture des statistiques de synchronisation"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permet à une application d\'accéder aux statistiques de synchronisation d\'un compte, y compris l\'historique des événements de synchronisation et le volume de données synchronisées."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"Voir le contenu de la mémoire USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"voir le contenu de la carte SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permettre de lire contenu de la mémoire de stockage USB"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permettre à l\'application de lire le contenu de la carte SD"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"Modifier ou supprimer le contenu de la mémoire USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifier ou supprimer le contenu de la carte SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permet à l\'application de modifier le contenu de la mémoire de stockage USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet à l\'application de modifier le contenu de la carte SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"lire contenu mémoire de stockage partagée"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permet de lire le contenu mémoire de stockage partagée."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modifier/supprimer contenu mémoire stockage partagée"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Permet de modifier le contenu mémoire de stockage partagée."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"effectuer/recevoir des appels SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Autorise l\'application à effectuer et à recevoir des appels SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"enregistrer de nouvelles connexions SIM de télécommunication"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifier un événement à l\'heure sélectionnée"</string>
<string name="view_flight" msgid="7691640491425680214">"Suivre"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Obtenir des informations sur le vol sélectionné"</string>
+ <string name="translate" msgid="9218619809342576858">"Traduire"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduire le texte sélectionné"</string>
+ <string name="define" msgid="7394820043869954211">"Définir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Définir le texte sélectionné"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"OK"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Effacement de la mémoire de stockage USB en cours…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Effacement de la carte SD en cours…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Suppression de l\'espace de stockage partagé…"</string>
<string name="share" msgid="1778686618230011964">"Partager"</string>
<string name="find" msgid="4808270900322985960">"Rechercher"</string>
<string name="websearch" msgid="4337157977400211589">"Recherche Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Requête SS transformée en requête USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Remplacement par une nouvelle requête SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil professionnel"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alerte envoyée"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Développer"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Réduire"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"activer/désactiver le développement"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 7f5a244..c5a4066 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wifi de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Chamadas por wifi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi de <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Chamadas por wifi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wifi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Chamadas por wifi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivado"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wifi preferida"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Datos móbiles preferidos"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que a aplicación lea as localizacións da túa colección multimedia."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"A aplicación <xliff:g id="APP">%s</xliff:g> quere que te autentiques."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"O hardware biométrico non está dispoñible"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Non se recoñeceu"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Detectouse unha impresión dixital parcial. Téntao de novo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impresión dixital está sucio. Límpao e téntao de novo."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"O dedo moveuse demasiado lento. Téntao de novo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Non se recoñeceu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autenticouse a impresión dixital"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Autenticouse a cara"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Autenticouse a cara, preme Confirmar"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permite a unha aplicación modificar a configuración de sincronización dunha conta. Por exemplo, esta acción pode utilizarse para activar a sincronización da aplicación Contactos cunha conta."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ler as estatísticas de sincronización"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permite a unha aplicación ler as estatísticas de sincronización dunha conta, incluído o historial de eventos de sincronización e a cantidade de datos sincronizados."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ler contidos do teu USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ler os contidos da túa tarxeta SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permite á aplicación ler o contido do teu almacenamento USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite á aplicación ler o contido da túa tarxeta SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar/eliminar contido USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar ou eliminar os contidos da túa tarxeta SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite á aplicación escribir no almacenamento USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite á aplicación escribir na tarxeta SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ler o almacenamento compartido"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permite á aplicación ler o almacenamento compartido."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modificar ou eliminar o almacenamento compartido"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Permite á aplicación escribir no almacenamento compartido."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"facer/recibir chamadas SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permite que a aplicación faga e reciba chamadas SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"rexistrar novas conexións SIM de telecomunicacións"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programa un evento para a data seleccionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Realizar seguimento"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Fai un seguimento do voo seleccionado"</string>
+ <string name="translate" msgid="9218619809342576858">"Traducir"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduce o texto seleccionado"</string>
+ <string name="define" msgid="7394820043869954211">"Definir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Define o texto seleccionado"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Estase esgotando o espazo de almacenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"É posible que algunhas funcións do sistema non funcionen"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">Unha coincidencia</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Feito"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Borrando o almacenamento USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Borrando a tarxeta SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Borrando almacenamento compartido…"</string>
<string name="share" msgid="1778686618230011964">"Compartir"</string>
<string name="find" msgid="4808270900322985960">"Buscar"</string>
<string name="websearch" msgid="4337157977400211589">"Busca na web"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"A solicitude SS transformouse nunha solicitude USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Transformouse nunha nova solicitude SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de traballo"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Con son"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Despregar"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Contraer"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"alterna a expansión"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5f98ff2..4908ed2 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> વાઇ-ફાઇ"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"વાઇ-ફાઇ કૉલિંગ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"વાઇ-ફાઇ કૉલિંગ"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"વાઇ-ફાઇ"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"વાઇ-ફાઇ કૉલિંગ"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"બંધ"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"વાઇ-ફાઇ પસંદ કર્યું"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"મોબાઇલને પસંદગી"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"એપને તમારા મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવાની મંજૂરી આપે છે."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ઍપ પ્રમાણીકરણ કરવા માગે છે."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ઓળખાયેલ નથી"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"આંશિક ફિંગરપ્રિન્ટ મળી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ફિંગરપ્રિન્ટ પ્રક્રિયા કરી શકાઈ નથી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ફિંગરપ્રિન્ટ સેન્સર ગંદું છે. કૃપા કરીને સાફ કરો અને ફરી પ્રયાસ કરો."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"આંગળી બહુ જ ધીમેથી ખસેડી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"ઓળખાયેલ નથી"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ચહેરા પ્રમાણિત"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ચહેરા પ્રમાણિત, કૃપા કરીને કન્ફર્મ કરો"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"એપ્લિકેશનને એકાઉન્ટ માટે સમન્વયન સેટિંગ્સ સંશોધિત કરવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે, આનો ઉપયોગ એકાઉન્ટ સાથે લોકો એપ્લિકેશનના સમન્વયનને સક્ષમ કરવા માટે થઈ શકે છે."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"સમન્વયન આંકડા વાંચો"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"એપ્લિકેશનને સમન્વયન ઇવેન્ટ્સનો ઇતિહાસ અને કેટલો ડેટા સમન્વયિત થયો છે તે સહિત કોઈ એકાઉન્ટ માટેનાં સમન્વયન આંકડા વાંચવાની મંજૂરી આપે છે."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"તમારા USB સંગ્રહની સામગ્રીઓ વાંચો"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"તમારા SD કાર્ડની સામગ્રીઓ વાંચો"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"એપ્લિકેશનને તમારા USB સંગ્રહની સામગ્રીઓ વાંચવાની મંજૂરી આપે છે."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"એપ્લિકેશનને તમારા SD કાર્ડની સામગ્રીઓ વાંચવાની મંજૂરી આપે છે."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"તમારા USB સંગ્રહની સામગ્રીઓ સંશોધિત કરો અથવા કાઢી નાખો"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"તમારા SD કાર્ડની સામગ્રીઓ સંશોધિત કરો અથવા કાઢી નાખો"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ઍપ્લિકેશનને USB સ્ટોરેજ પર લખવાની મંજૂરી આપે છે."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"એપ્લિકેશનને SD કાર્ડ પર લખવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"તમારા શેર કરેલા સ્ટોરેજના કન્ટેન્ટને વાંચો"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"શેર કરેલા સ્ટોરેજ કન્ટેન્ટને વાંચવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"શેર કરેલા સ્ટોરેજ કન્ટેન્ટમાં ફેરફાર કરો/ડિલીટ કરો"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"શેર કરેલા સ્ટોરેજ કન્ટેન્ટમાં લખવાની મંજૂરી આપે છે."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP કૉલ્સ કરો/પ્રાપ્ત કરો"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"એપ્લિકેશનને SIP કૉલ્સ કરવા અને પ્રાપ્ત કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"નવા ટેલિકોમ સિમ કનેક્શન્સની નોંધણી કરો"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"પસંદ કરેલ સમય માટે ઇવેન્ટ શેડ્યૂલ કરો"</string>
<string name="view_flight" msgid="7691640491425680214">"ટ્રૅક કરો"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"પસંદ કરેલ ફ્લાઇટને ટ્રૅક કરો"</string>
+ <string name="translate" msgid="9218619809342576858">"અનુવાદ કરો"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"પસંદ કરેલી ટેક્સ્ટનો અનુવાદ કરો"</string>
+ <string name="define" msgid="7394820043869954211">"વ્યાખ્યાતિત કરો"</string>
+ <string name="define_desc" msgid="7910883642444919726">"પસંદ કરેલી ટેક્સ્ટને વ્યાખ્યાતિત કરો"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"કેટલાક સિસ્ટમ Tasks કામ કરી શકશે નહીં"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g> માંથી <xliff:g id="INDEX">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"થઈ ગયું"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB સ્ટોરેજ ભૂસી રહ્યાં છીએ…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD કાર્ડ કાઢી નાખી રહ્યાં છે…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"શેર કરેલ સ્ટોરેજ ભૂસી રહ્યાં છીએ…"</string>
<string name="share" msgid="1778686618230011964">"શેર કરો"</string>
<string name="find" msgid="4808270900322985960">"શોધો"</string>
<string name="websearch" msgid="4337157977400211589">"વેબ શોધ"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS વિનંતીને USSD વિનંતીમાં બદલવામાં આવી છે"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"નવી SS વિનંતીમાં બદલવામાં આવી છે"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"કાર્યાલયની પ્રોફાઇલ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"અલર્ટ કરેલ"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"વિસ્તૃત કરો"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"સંકુચિત કરો"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"વિસ્તરણ ટૉગલ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 92e3ae5..87e1f97 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> वाई-फ़ाई"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"वाई-फ़ाई कॉलिंग | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"वाई-फ़ाई कॉलिंग"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"वाई-फ़ाई"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"वाई-फ़ाई कॉलिंग"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"बंद"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"वाई-फ़ाई को प्राथमिकता"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"मोबाइल को प्राथमिकता"</string>
@@ -310,7 +306,7 @@
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"कॉल लॉग की जानकारी देखना और उसमें बदलाव करना"</string>
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपने काॅल लाॅग एक्सेस करने की मंज़ूरी देना चाहते हैं?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"फ़ोन"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"फ़ोन कॉल करें और प्रबंधित करें"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें"</string>
<string name="permgrouprequest_phone" msgid="9166979577750581037">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"शरीर संवेदक"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें"</string>
@@ -426,8 +422,8 @@
<string name="permlab_accessFineLocation" msgid="6265109654698562427">"ऐप्लिकेशन \'जगह की सटीक जानकारी\' सिर्फ़ सामने खुली होने पर एक्सेस करे"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"यह ऐप्लिकेशन सिर्फ़ तब आपकी \'जगह की सटीक जानकारी\' का इस्तेमाल कर सकता है जब यह स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके फ़ोन में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए. ऐसा करने से ज़्यादा बैटरी खर्च हो सकती है."</string>
<string name="permlab_accessCoarseLocation" msgid="3707180371693213469">"स्क्रीन पर दिखाई देते समय \'जगह की अनुमानित जानकारी\' (नेटवर्क-आधारित) एक्सेस करें"</string>
- <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"यह ऐप्लिकेशन सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क स्रोतों के आधार पर आपकी जगह का पता लगा सकता है, लेकिन सिर्फ़ तब जब ऐप्लिकेशन स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके टैबलेट में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए."</string>
- <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"यह ऐप्लिकेशन सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क स्रोतों के आधार पर आपकी जगह का पता लगा सकता है, लेकिन सिर्फ़ तब जब ऐप्लिकेशन स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके टीवी पर मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए."</string>
+ <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"यह ऐप्लिकेशन सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क स्रोतों के आधार पर आपकी जगह का पता लगा सकता है, लेकिन सिर्फ़ तब, जब ऐप्लिकेशन स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके टैबलेट में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए."</string>
+ <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"यह ऐप्लिकेशन सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क स्रोतों के आधार पर आपकी जगह का पता लगा सकता है, लेकिन सिर्फ़ तब, जब ऐप्लिकेशन स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके टीवी पर मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए."</string>
<string name="permdesc_accessCoarseLocation" product="default" msgid="854896049371048754">"यह ऐप्लिकेशन सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क स्रोतों के आधार पर आपकी जगह का पता लगा सकता है, लेकिन सिर्फ़ तब, जब ऐप्लिकेशन स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके फ़ोन में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए."</string>
<string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"बैकग्राउंड में जगह की जानकारी एक्सेस करना"</string>
<string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"अनुमानित या बिल्कुल सही जगह की जानकारी का एक्सेस करने की अनुमति अलग से दिए जाने पर, बैकग्राउंड में चलने के दौरान ऐप्लिकेशन आपकी जगह की जानकारी एक्सेस कर सकता है."</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"इससे ऐप्लिकेशन को आपके मीडिया संग्रह से जगह की जानकारी एक्सेस करने की अनुमति दी जाती है."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"ऐप्लिकेशन <xliff:g id="APP">%s</xliff:g> पुष्टि करना चाहता है."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेयर उपलब्ध नहीं है"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"पहचान नहीं हो पाई"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फ़िंगरप्रिंट की पहचान की गई. कृपया पुनः प्रयास करें."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट संसाधित नहीं हो सका. कृपया पुन: प्रयास करें."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फ़िंगरप्रिंट सेंसर गंदा है. कृपया साफ़ करें और फिर कोशिश करें."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"उंगली बहुत धीरे चलाई गई. कृपया फिर से कोशिश करें."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"पहचान नहीं हो पाई"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"चेहरे की पहचान की गई"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ऐप्स को किसी खाते की समन्वयन सेटिंग संशोधित करने देता है. उदाहरण के लिए, इसका उपयोग लोग ऐप्स का समन्वयन किसी खाते से सक्षम करने में हो सकता है."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"समन्वयन आंकड़े पढ़ें"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ऐप्स को किसी खाते के समन्वयन आंकड़े, साथ ही समन्वयित इवेंट का इतिहास और समन्वयित डेटा की मात्रा पढ़ने देता है."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"अपने USB मेमोरी की सामग्री पढ़ें"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"अपने SD कार्ड की सामग्री पढ़ें"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"एप्लिकेशन को आपके USB मेमोरी की सामग्री पढ़ने की अनुमति देता है."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"एप्लिकेशन को आपके SD कार्ड की सामग्री पढ़ने की अनुमति देता है."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"अपने USB मेमोरी की सामग्री बदलें या मिटाएं"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"अपने SD कार्ड की सामग्री बदलें या मिटाएं"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ऐप्स को USB मेमोरी में लिखने देता है."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"ऐप्स को SD कार्ड पर लिखने देता है."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"आपकी शेयर की गई मेमोरी की सामग्री पढ़ना"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"ऐप्लिकेशन को आपकी शेयर की गई मेमोरी की सामग्री पढ़ने देती है."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"आपकी शेयर की गई मेमोरी की सामग्री में बदलाव करना या उसे मिटाना"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"ऐप्लिकेशन को आपकी शेयर की गई मेमोरी की सामग्री लिखने देती है."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP कॉल करें/प्राप्त करें"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ऐप्स को SIP कॉल करने और प्राप्त करने देती है."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"नए टेलिकॉम सिम कनेक्शन रजिस्टर करें"</string>
@@ -650,23 +646,23 @@
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"उपयोगकर्ता को किसी मोबाइल और इंटरनेट सेवा देने वाली कंपनी से जोड़ता है. सामान्य ऐप के लिए इसकी कभी ज़रूरत नहीं होती."</string>
<string name="permlab_access_notification_policy" msgid="4247510821662059671">"\'परेशान न करें\' को ऐक्सेस करें"</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"ऐप को परेशान न करें कॉन्फ़िगरेशन पढ़ने और लिखने देती है."</string>
- <string name="policylab_limitPassword" msgid="4497420728857585791">"पासवर्ड नियम सेट करें"</string>
- <string name="policydesc_limitPassword" msgid="2502021457917874968">"स्क्रीन लॉक पासवर्ड तथा पिन की लंबाई और उसमें अनुमत वर्णों को नियंत्रित करें."</string>
- <string name="policylab_watchLogin" msgid="5091404125971980158">"स्क्रीन अनलॉक करने की कोशिशों की निगरानी करें"</string>
+ <string name="policylab_limitPassword" msgid="4497420728857585791">"पासवर्ड नियम सेट करना"</string>
+ <string name="policydesc_limitPassword" msgid="2502021457917874968">"स्क्रीन लॉक पासवर्ड और पिन की लंबाई और उनमें स्वीकृत वर्णों को नियंत्रित करना."</string>
+ <string name="policylab_watchLogin" msgid="5091404125971980158">"स्क्रीन अनलॉक करने के की कोशिशों पर नज़र रखना"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"स्क्रीन को अनलॉक करते समय गलत लिखे गए पासवर्ड की संख्या पर निगरानी करें, और बहुत अधिक बार गलत पासवर्ड लिखे जाने पर टैबलेट लॉक करें या टैबलेट का संपूर्ण डेटा मिटाएं."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"स्क्रीन को अनलॉक करते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें और यदि बहुत अधिक गलत पासवर्ड लिखे जाते हैं तो टीवी को लॉक करें या टीवी का सभी डेटा मिटा दें."</string>
- <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"गलत लिखे गए पासवर्ड की संख्या पर निगरानी करें. स्क्रीन अनलॉक करते समय, बहुत अधिक बार गलत पासवर्ड लिखे जाने पर फ़ोन लॉक करें या फ़ोन का संपूर्ण डेटा मिटाएं."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"स्क्रीन को अनलॉक करते समय जितनी बार गलत पासवर्ड लिखा गया है, उसकी संख्या पर नज़र रखना और अगर बहुत बार गलत पासवर्ड डाले गए हैं, तो फ़ोन को लॉक कर देना या फ़ोन का सारा डेटा मिटा देना."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार अधिक पासवर्ड लिखे जाते हैं तो टैबलेट को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार गलत पासवर्ड लिखा जाता है तो टीवी को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"स्क्रीनका लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार गलत पासवर्ड लिखा जाता है तो फ़ोन को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
- <string name="policylab_resetPassword" msgid="4934707632423915395">"स्क्रीन लॉक बदलें"</string>
- <string name="policydesc_resetPassword" msgid="1278323891710619128">"स्क्रीन लॉक को बदलें."</string>
+ <string name="policylab_resetPassword" msgid="4934707632423915395">"स्क्रीन लॉक बदलना"</string>
+ <string name="policydesc_resetPassword" msgid="1278323891710619128">"स्क्रीन लॉक बदलना."</string>
<string name="policylab_forceLock" msgid="2274085384704248431">"स्क्रीन लॉक करें"</string>
<string name="policydesc_forceLock" msgid="1141797588403827138">"नियंत्रित करें कि स्क्रीन कैसे और कब लॉक हो."</string>
- <string name="policylab_wipeData" msgid="3910545446758639713">"सभी डेटा मिटाएं"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना टैबलेट का डेटा मिटाएं."</string>
- <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना टीवी का डेटा मिटाएं."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना फ़ोन का डेटा मिटाएं."</string>
+ <string name="policylab_wipeData" msgid="3910545446758639713">"सारा डेटा मिटाना"</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"फ़ैक्टरी डेटा रीसेट करके चेतावनी दिए बिना फ़ोन का डेटा मिटाना."</string>
+ <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"फ़ैक्टरी डेटा रीसेट करके चेतावनी दिए बिना फ़ोन का डेटा मिटाना."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"फ़ैक्टरी डेटा रीसेट करके चेतावनी दिए बिना फ़ोन का डेटा मिटाना."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"उपयोगकर्ता डेटा मिटाएं"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"इस टैबलेट पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"इस टीवी पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"चुने गए समय के लिए इवेंट शेड्यूल करें"</string>
<string name="view_flight" msgid="7691640491425680214">"मौजूदा स्थिति जानें"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"चुनी गई फ़्लाइट की मौजूदा स्थिति देखें"</string>
+ <string name="translate" msgid="9218619809342576858">"अनुवाद करें"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"चुने गए टेक्स्ट का अनुवाद करें"</string>
+ <string name="define" msgid="7394820043869954211">"परिभाषित करें"</string>
+ <string name="define_desc" msgid="7910883642444919726">"चुना गया टेक्स्ट परिभाषित करें"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"मेमोरी में जगह नहीं बची है"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g> में से <xliff:g id="INDEX">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"हो गया"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB मेमोरी मिटाया जा रहा है…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD कार्ड मिटाया जा रहा है…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"शेयर की गई मेमोरी हमेशा के लिए मिटाई जा रही है…"</string>
<string name="share" msgid="1778686618230011964">"शेयर करें"</string>
<string name="find" msgid="4808270900322985960">"ढूंढें"</string>
<string name="websearch" msgid="4337157977400211589">"वेब सर्च"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"एसएस कोड चलाने के अनुरोध को यूएसएसडी कोड चलाने के अनुरोध में बदला गया"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"एसएस कोड चलाने के नए अनुरोध में बदला गया"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"कार्य प्रोफ़ाइल"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"अलर्ट किया गया"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"विस्तार करें"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"छोटा करें"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"टॉगल विस्तार"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 7e581a20..ff2afba 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -137,14 +137,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi pozivi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi pozivi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi pozivi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Isključeno"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Prednost ima Wi-Fi mreža"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Za mobilne uređaje"</string>
@@ -536,6 +532,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Omogućuje aplikaciji čitanje lokacija iz vaše medijske zbirke."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> traži autentifikaciju."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomični otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otiska prsta nije čist. Očistite ga i pokušajte ponovo."</string>
@@ -543,7 +544,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Presporo pomicanje prsta. Pokušajte ponovo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autentificirano otiskom prsta"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Lice je autentificirano"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Lice je autentificirano, pritisnite Potvrdi"</string>
@@ -599,14 +599,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Aplikaciji omogućuje izmjenu postavki sinkronizacije za račun. Na primjer, to se može upotrijebiti za omogućavanje sinkronizacije aplikacije Osobe s računom."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"čitanje statistike o sinkronizaciji"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Aplikaciji omogućuje čitanje statistika sinkronizacije za račun, uključujući povijest sinkronizacije te količinu sinkroniziranih podataka."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"čitanje sadržaja USB pohrane"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"čitanje sadržaja SD kartice"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Aplikaciji omogućuje čitanje sadržaja vaše USB pohrane."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Aplikaciji omogućuje čitanje sadržaja vaše SD kartice."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"izmjena/brisanje sadržaja USB-a"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"izmjena ili brisanje sadržaja SD kartice"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Dopušta pisanje u USB pohranu."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Aplikaciji omogućuje pisanje na SD karticu."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"čitanje sadržaja dijeljene pohrane"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Aplikaciji omogućuje čitanje sadržaja vaše dijeljene pohrane."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"izmjena ili brisanje sadržaja dijeljene pohrane"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Aplikaciji omogućuje pisanje sadržaja u dijeljenu pohranu."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"upućivanje/primanje SIP poziva"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Aplikacija može upućivati i primati SIP pozive."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registriranje novih telekomunikacijskih SIM veza"</string>
@@ -1125,6 +1121,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakazivanje događaja za određeno vrijeme"</string>
<string name="view_flight" msgid="7691640491425680214">"Prati"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Praćenje odabranog leta"</string>
+ <string name="translate" msgid="9218619809342576858">"Prevedi"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevedite odabrani tekst"</string>
+ <string name="define" msgid="7394820043869954211">"Definiraj"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definiraj odabrani tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string>
@@ -1483,8 +1483,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> od <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Gotovo"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Brisanje USB pohrane..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Brisanje SD kartice..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Brisanje dijeljene pohrane…"</string>
<string name="share" msgid="1778686618230011964">"Dijeli"</string>
<string name="find" msgid="4808270900322985960">"Pronađi"</string>
<string name="websearch" msgid="4337157977400211589">"Pretraž. weba"</string>
@@ -1866,8 +1865,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS zahtjev promijenjen je u USSD zahtjev"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Promijenjeno u novi SS zahtjev"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Radni profil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Upozoreni"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Proširivanje"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Sažimanje"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"promjena proširenja"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 41bf09b..59d5123 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi-hívás | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi-hívás"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi-hívás"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Ki"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi előnyben részesítve"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferált: mobil"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Engedélyezi az alkalmazásnak a helyek médiagyűjteményből való olvasását."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás hitelesítést kér."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrikus hardver nem áll rendelkezésre"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nem ismerhető fel"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"A rendszer az ujjlenyomatnak csak egy részletét érzékelte. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Az ujjlenyomat-olvasó koszos. Tisztítsa meg, majd próbálkozzon újra."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Túl lassan húzta az ujját. Próbálkozzon újra."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nem ismerhető fel"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Ujjlenyomat hitelesítve"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Arc hitelesítve"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Arc hitelesítve; nyomja meg a Megerősítés lehetőséget"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Lehetővé teszi az alkalmazás számára egy fiók szinkronizálási beállításainak módosítását. Például ezzel engedélyezni lehet a Személyek alkalmazás szinkronizálását egy fióknál."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"szinkronizálási statisztikák olvasása"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Lehetővé teszi az alkalmazás számára egy fiók szinkronizálási statisztikáinak a beolvasását, beleértve a szinkronizálási események előzményeit, valamint a szinkronizált adatok mennyiségét."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB-tár tartalmának olvasása"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD-kártya tartalmának olvasása"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Beolvashat USB-tárakat."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Lehetővé teszi az alkalmazás számára az SD-kártya tartalmának beolvasását."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB-tár törlése/módosítása"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD-kártya tartalmának módosítása vagy törlése"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Az alkalmazás USB-tárra írhat."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lehetővé teszi az alkalmazás számára, hogy írjon az SD-kártyára."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"közös tárhely olvasása"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Engedélyezi az alkalmazásnak a közös tárhely tartalmainak olvasását."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"a közös tárhely tartalmainak törlése és módosítása"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Engedélyezi az alkalmazás számára a közös tárhely tartalmainak felülírását."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP-hívások indítása/fogadása"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP-hívások indításának és fogadásának engedélyezése az alkalmazás számára."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"új telekommunikációs SIM kapcsolatok regisztrálása"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Esemény ütemezése a kiválasztott időpontra"</string>
<string name="view_flight" msgid="7691640491425680214">"Nyomon követés"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Kiválasztott járat nyomon követése"</string>
+ <string name="translate" msgid="9218619809342576858">"Fordítás"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"A kijelölt szöveg fordítása"</string>
+ <string name="define" msgid="7394820043869954211">"Definiálás"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Kijelölt szöveg definiálása"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 találat</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Kész"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Az USB-tár törlése..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Az SD-kártya törlése..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Közös tárhely tartalmának törlése…"</string>
<string name="share" msgid="1778686618230011964">"Megosztás"</string>
<string name="find" msgid="4808270900322985960">"Keresés"</string>
<string name="websearch" msgid="4337157977400211589">"Webes keresés"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Az SS-kérés módosítva USSD-kérésre"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Új SS-kérésre módosítva"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Munkaprofil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Értesítve"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Kibontás"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Összecsukás"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"kibontás be- és kikapcsolása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 30721cf..d019f0e 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Զանգեր Wi-Fi-ի միջոցով | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Զանգեր Wi-Fi-ի միջոցով"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Զանգեր Wi-Fi-ի միջոցով"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Անջատված է"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi, նախընտրելի"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Նախընտրելի է բջջային ցանցը"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Թույլ է տալիս հավելվածին ճանաչել տեղադրության մասին տվյալները ձեր մեդիա բովանդակության հավաքածուից:"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> հավելվածը նույնականացում է հարցում"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Կենսաչափական սարքը հասանելի չէ"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Չհաջողվեց ճանաչել"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Շատ դանդաղ անցկացրեցիք մատը: Փորձեք նորից:"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Չհաջողվեց ճանաչել"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Մատնահետքը նույնականացվեց"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Դեմքը ճանաչվեց"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Թույլ է տալիս հավելվածին փոփոխել համաժամեցման կարգավորումները հաշվի համար: Օրինակ, այն կարող է օգտագործվել` միացնելու Մարդիկ հավելվածի համաժամեցումը հաշվի հետ:"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"կարդալ համաժամեցման վիճակագրությունը"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Թույլ է տալիս հավելվածին կարդալ հաշվի համաժամեցման վիճակագրությունը, այդ թվում` համաժամեցման իրադարձությունների պատմությունը և թե որքան տվյալ է համաժամեցված:"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"կարդալ ձեր USB կրիչի պարունակությունը"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"կարդալ ձեր SD քարտի պարունակությունը"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Հավելվածին թույլ է տալիս կարդալ ձեր USB կրիչի պարունակությունը:"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Հավելվածին թույլ է տալիս կարդալ ձեր SD քարտի պարունակությունը:"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"փոփոխել կամ ջնջել ձեր USB կրիչի բովանդակությունը"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"փոփոխել կամ ջնջել ձեր SD քարտի բովանդակությունը"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Թույլ է տալիս հավելվածին գրել USB կրիչի վրա:"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Թույլ է տալիս հավելվածին գրել SD քարտի վրա:"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"կարդալ ձեր ընդհանուր հիշողության պարունակությունը"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Հավելվածին թույլ է տալիս կարդալ ձեր ընդհանուր հիշողության պարունակությունը:"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"փոփոխել կամ ջնջել ձեր ընդհանուր հիշողության բովանդակությունը"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Հավելվածին թույլ է տալիս փոփոխել ձեր ընդհանուր հիշողության պարունակությունը:"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"կատարել կամ ստանալ SIP զանգեր"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Ծրագրին թույլ է տալիս կատարել և ստանալ SIP զանգեր:"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"գրանցել նոր հեռահաղորդակցության SIM կապեր"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ստեղծել միջոցառում նշված օրվա համար"</string>
<string name="view_flight" msgid="7691640491425680214">"Հետագծել"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Հետագծել ընտրված չվերթը"</string>
+ <string name="translate" msgid="9218619809342576858">"Թարգմանել"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Թարգմանել ընտրված տեքստը"</string>
+ <string name="define" msgid="7394820043869954211">"Սահմանել"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Սահմանել ընտրված տեքստը"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Հիշողությունը սպառվում է"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Որոշ գործառույթներ կարող են չաշխատել"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g>՝ <xliff:g id="TOTAL">%d</xliff:g>-ից</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Պատրաստ է"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Ջնջում է USB կրիչը..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Ջնջում է SD քարտը..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Ընդհանուր հիշողությունը ջնջվում է…"</string>
<string name="share" msgid="1778686618230011964">"Կիսվել"</string>
<string name="find" msgid="4808270900322985960">"Գտնել"</string>
<string name="websearch" msgid="4337157977400211589">"Վեբի որոնում"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS հարցումը փոխվել է USSD հարցման"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Փոխվել է նոր SS հարցման"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Աշխատանքային պրոֆիլ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Ուղարկվել է զգուշացում"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Ընդարձակել"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Կոծկել"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"Կոծկել/Ընդարձակել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 49c80d7..cc70724 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Panggilan WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Panggilan Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Panggilan WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Nonaktif"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi dipilih"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Seluler dipilih"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Mengizinkan aplikasi untuk membaca lokasi dari koleksi media Anda."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikasi <xliff:g id="APP">%s</xliff:g> meminta autentikasi."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrik tidak tersedia"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sebagian sidik jari terdeteksi. Coba lagi."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses sidik jari. Coba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensor sidik jari kotor. Bersihkan dan coba lagi."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Jari digerakkan terlalu lambat. Coba lagi."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sidik jari diautentikasi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Wajah diautentikasi"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Wajah diautentikasi, silakan tekan konfirmasi"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Memungkinkan aplikasi mengubah setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat digunakan untuk mengaktifkan sinkronisasi dari aplikasi Orang dengan sebuah akun."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"statistika baca sinkron"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Memungkinkan aplikasi membaca statistik sinkronisasi untuk sebuah akun, termasuk histori kejadian sinkronisasi dan berapa banyak data yang disinkronkan."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"baca konten simpanan USB Anda"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"baca konten kartu SD Anda"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Mengizinkan aplikasi membaca konten penyimpanan USB Anda."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Mengizinkan aplikasi membaca konten kartu SD Anda."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ubah/hapus isi penyimpanan USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"mengubah atau menghapus konten kartu SD Anda"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Mengizinkan apl menulis ke penyimpanan USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Memungkinkan apl menulis ke kartu SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"membaca konten penyimpanan bersama Anda"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Mengizinkan aplikasi membaca konten penyimpanan bersama Anda."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"memodifikasi atau menghapus konten penyimpanan bersama Anda"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Mengizinkan aplikasi menulis konten penyimpanan bersama Anda."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"lakukan/terima panggilan SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Izinkan aplikasi melakukan dan menerima panggilan SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"daftarkan sambungan SIM telekomunikasi baru"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Menjadwalkan acara untuk waktu yang dipilih"</string>
<string name="view_flight" msgid="7691640491425680214">"Lacak"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Memantau penerbangan yang dipilih"</string>
+ <string name="translate" msgid="9218619809342576858">"Terjemahkan"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Terjemahkan teks yang dipilih"</string>
+ <string name="define" msgid="7394820043869954211">"Tentukan"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Tentukan teks yang dipilih"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 kecocokan</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Selesai"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Menghapus penyimpanan USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Menghapus kartu SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Menghapus penyimpanan bersama…"</string>
<string name="share" msgid="1778686618230011964">"Bagikan"</string>
<string name="find" msgid="4808270900322985960">"Temukan"</string>
<string name="websearch" msgid="4337157977400211589">"Penelusuran Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Permintaan SS diubah ke permintaan USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Diubah ke permintaan SS baru"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil kerja"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Diingatkan"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Luaskan"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Ciutkan"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"beralih ke perluasan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index a33c80c..72ce56c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi símtal | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi símtöl"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WiFi símtöl"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Slökkt"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi í forgangi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Farsímakerfi í forgangi"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Leyfir forritinu að lesa staðsetningar úr efnissafninu þínu."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> forritið vill staðfestingu."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Lífkennavélbúnaður ekki tiltækur"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Þekktist ekki"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hluti fingrafars greindist. Reyndu aftur."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingrafaraskynjarinn er óhreinn. Hreinsaðu hann og reyndu aftur."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Fingurinn hreyfðist of hægt. Reyndu aftur."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Þekktist ekki"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingrafar staðfest"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Andlit staðfest"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Andlit staðfest, ýttu til að staðfesta"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Leyfir forriti að breyta kostum samstillingar fyrir reikning. Þetta er til dæmis hægt að nota til að kveikja á samstillingu forritsins Fólk við reikning."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lesa talnagögn um samstillingu"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Leyfir forriti að lesa talnagögn samstillingar fyrir reikning, þ. á m. feril samstillingaratvika og samstillt gagnamagn."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"lesa innihald USB-geymslunnar"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lesa innihald SD-kortsins"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Leyfir forriti að lesa innihald USB-geymslunnar."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Leyfir forriti að lesa innihald SD-kortsins."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"breyta eða eyða innihaldi USB-geymslunnar"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"breyta eða eyða innihaldi SD-kortsins"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Leyfir forriti að skrifa í USB-geymsluna."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Leyfir forriti að skrifa á SD-kortið."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"lesa innihald samnýtta geymslurýmisins"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Leyfir forritinu að lesa innihald samnýtta geymslurýmisins."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"breyta eða eyða innihaldi samnýtta geymslurýmisins"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Leyfir forriti að skrifa í innihald samnýtta geymslurýmisins."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"hringja/svara SIP-símtölum"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Leyfir forritinu að hringja og svara SIP-símtölum."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"skrá nýjar símatengingar fyrir SIM-kort"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Skipuleggja viðburð á völdum tíma"</string>
<string name="view_flight" msgid="7691640491425680214">"Rekja"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Fylgjast með völdu flugi"</string>
+ <string name="translate" msgid="9218619809342576858">"Þýða"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Þýða valinn texta"</string>
+ <string name="define" msgid="7394820043869954211">"Skilgreina"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Skilgreina valinn texta"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Geymslurýmið er senn á þrotum"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> af <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Lokið"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Hreinsar út af USB-geymslu…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Hreinsar út af SD-korti…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Eyðir samnýttri geymslu…"</string>
<string name="share" msgid="1778686618230011964">"Deila"</string>
<string name="find" msgid="4808270900322985960">"Finna"</string>
<string name="websearch" msgid="4337157977400211589">"Vefleit"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-beiðni breytt í USSD-beiðni"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Breytt í nýja SS-beiðni"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Vinnusnið"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Tilkynnt"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Stækka"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Minnka"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"stækka eða minnka"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 7c78677..4463e11 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Chiamate Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Chiamate Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Chiamate Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Non attiva"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Rete preferita: Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Rete preferita: dati mobili"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Consente all\'app di leggere le posizioni dalla tua raccolta multimediale."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"L\'app <xliff:g id="APP">%s</xliff:g> richiede l\'autenticazione."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrico non disponibile"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Non riconosciuto"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Rilevata impronta digitale parziale. Riprova."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossibile elaborare l\'impronta digitale. Riprova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Il sensore di impronte digitali è sporco. Puliscilo e riprova."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Movimento del dito troppo lento. Riprova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Non riconosciuto"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Impronta digitale autenticata"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Volto autenticato"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Volto autenticato, premi Conferma"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Consente a un\'applicazione di modificare le impostazioni di sincronizzazione per un account. Ad esempio, può servire per attivare la sincronizzazione dell\'applicazione Persone con un account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lettura statistiche di sincronizz."</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Consente a un\'applicazione di leggere le statistiche di sincronizzazione per un account, incluse la cronologia degli eventi di sincronizzazione e la quantità di dati sincronizzati."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"lettura contenuti archivio USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lettura contenuti scheda SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"L\'app può leggere i contenuti dell\'archivio USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Consente all\'applicazione di leggere i contenuti della scheda SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifica/eliminazione contenuti USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifica o eliminazione dei contenuti della scheda SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Consente all\'applicazione di scrivere nell\'archivio USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Consente all\'applicazione di scrivere sulla scheda SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"leggere i contenuti dell\'archivio condiviso"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Consente all\'app di leggere i contenuti del tuo archivio condiviso."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modificare/eliminare i contenuti dell\'archivio condiviso"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Consente all\'app di modificare i contenuti del tuo archivio condiviso."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"fare/ricevere chiamate SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Consente all\'app di effettuare e ricevere chiamate SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrazione di nuove connessioni SIM di telecomunicazione"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Pianifica l\'evento nella data selezionata"</string>
<string name="view_flight" msgid="7691640491425680214">"Monitora"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Monitora il volo selezionato"</string>
+ <string name="translate" msgid="9218619809342576858">"Traduci"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduci il testo selezionato"</string>
+ <string name="define" msgid="7394820043869954211">"Definisci"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definisci il testo selezionato"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 partita</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Fine"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Cancellazione dell\'archivio USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Cancellazione scheda SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Cancellazione archivio condiviso…"</string>
<string name="share" msgid="1778686618230011964">"Condividi"</string>
<string name="find" msgid="4808270900322985960">"Trova"</string>
<string name="websearch" msgid="4337157977400211589">"Ricerca Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Richiesta SS modificata in richiesta USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Modificata in nuova richiesta SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profilo di lavoro"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Avviso inviato"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Espandi"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Comprimi"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"attiva/disattiva l\'espansione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 6034ee0..3f0c0b8 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"שיחות WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"שיחות Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"שיחות Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"כבוי"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi מועדף"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"מצב מועדף: רשת סלולרית"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"מאפשרת לאפליקציה לקרוא מיקומים מאוסף המדיה שלך."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"האפליקציה <xliff:g id="APP">%s</xliff:g> רוצה לבצע אימות."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"חומרה ביומטרית לא זמינה"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"לא זוהתה"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"זוהתה טביעת אצבע חלקית. נסה שוב."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"לא ניתן היה לעבד את טביעת האצבע. נסה שוב."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"החיישן של טביעות האצבעות מלוכלך. נקה אותו ונסה שוב."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"הזזת את האצבע לאט מדי. נסה שוב."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"לא זוהתה"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"טביעת האצבע אומתה"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"זיהוי הפנים בוצע"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"מאפשר לאפליקציה לשנות את הגדרות הסנכרון של חשבון. לדוגמה, ניתן להשתמש בכך על מנת להפעיל סנכרון של האפליקציה \'אנשים\' עם חשבון כלשהו."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"קרא את הנתונים הסטטיסטיים של הסינכרון"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"מאפשר לאפליקציה לקרוא את סטטיסטיקת הסנכרון של חשבון, כולל היסטוריית אירועי הסנכרון וכמות הנתונים שסונכרנה."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"קריאת התוכן של אחסון ה-USB שלך"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"קריאת התוכן של כרטיס ה-SD שלך"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"מאפשר לאפליקציה לקרוא את תוכן אחסון ה-USB שלך."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"מאפשר לאפליקציה לקרוא את תוכן כרטיס ה-SD שלך."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"שינוי או מחיקה של תוכן אחסון ה-USB שלך"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"שינוי או מחיקה של תוכן כרטיס ה-SD שלך"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"מאפשר לאפליקציה לכתוב להתקן האחסון מסוג USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"מאפשר לאפליקציה לכתוב לכרטיס ה-SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"קריאת התוכן של האחסון המשותף שלך"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"מאפשר לאפליקציה לקרוא את התוכן של האחסון המשותף."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"שינוי או מחיקה של תוכן האחסון המשותף שלך"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"מאפשר לאפליקציה לכתוב את התוכן של האחסון המשותף."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"ביצוע/קבלה של שיחות SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"אפשר לאפליקציה לבצע ולקבל שיחות SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"רשום חיבורי Telecom SIM חדשים"</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"תזמון אירוע לזמן שנבחר"</string>
<string name="view_flight" msgid="7691640491425680214">"מעקב"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ניהול מעקב אחר הטיסה שנבחרה"</string>
+ <string name="translate" msgid="9218619809342576858">"תרגום"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"תרגום הטקסט שנבחר"</string>
+ <string name="define" msgid="7394820043869954211">"הגדרה"</string>
+ <string name="define_desc" msgid="7910883642444919726">"הגדרת הטקסט שנבחר"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"אין מספיק שטח אחסון עבור המערכת. ודא שיש לך שטח פנוי בגודל 250MB התחל שוב."</string>
@@ -1506,8 +1506,7 @@
<item quantity="one">התאמה אחת</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"סיום"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"מוחק אחסון USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"מוחק כרטיס SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"בתהליך מחיקה של אחסון משותף…"</string>
<string name="share" msgid="1778686618230011964">"שתף"</string>
<string name="find" msgid="4808270900322985960">"מצא"</string>
<string name="websearch" msgid="4337157977400211589">"חיפוש באינטרנט"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"בקשת SS שונתה לבקשת USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"היה שינוי לבקשת SS חדשה"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"פרופיל עבודה"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"נשלחה התראה"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"הרחב"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"כווץ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"החלפת מצב הרחבה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4bebf20..e069e33 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi 通話 | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"OFF"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi優先"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"モバイル優先"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"メディア コレクションの位置情報の読み取りをアプリに許可します。"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"アプリ <xliff:g id="APP">%s</xliff:g> が認証を求めています。"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"生体認証ハードウェアが利用できません"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"認識されませんでした"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"指紋を一部しか検出できませんでした。もう一度お試しください。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"指紋を処理できませんでした。もう一度お試しください。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋認証センサーに汚れがあります。汚れを落としてもう一度お試しください。"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"指の動きが遅すぎました。もう一度お試しください。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"認識されませんでした"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"指紋認証を完了しました"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"顔を認証しました"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"顔を認証しました。[確認] を押してください"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"アカウントの同期設定の変更をアプリに許可します。たとえば、連絡帳アプリとアカウントの同期を有効にするために使用できます。"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"同期統計の読み取り"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"アカウントの同期ステータス(同期イベントの履歴、同期されたデータの量など)の読み取りをアプリに許可します。"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USBストレージの読み取り"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SDカードのコンテンツの読み取り"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"USBストレージのコンテンツの読み取りをアプリに許可します。"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"SDカードのコンテンツの読み取りをアプリに許可します。"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USBストレージのコンテンツの変更または削除"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SDカードのコンテンツの変更または削除"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USBストレージへの書き込みをアプリに許可します。"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SDカードへの書き込みをアプリに許可します。"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"共有ストレージのコンテンツの読み取り"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"共有ストレージのコンテンツの読み取りをアプリに許可します。"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"共有ストレージのコンテンツの変更または削除"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"共有ストレージのコンテンツの書き込みをアプリに許可します。"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP通話の発着信"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP通話の発着信をアプリに許可します。"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"新しい通信SIM接続の登録"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"選択した日時に予定を設定します"</string>
<string name="view_flight" msgid="7691640491425680214">"トラック"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"選択したフライトをチェックします"</string>
+ <string name="translate" msgid="9218619809342576858">"翻訳"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"選択したテキストを翻訳します"</string>
+ <string name="define" msgid="7394820043869954211">"辞書で調べる"</string>
+ <string name="define_desc" msgid="7910883642444919726">"選択したテキストを辞書で調べます"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1件一致</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"完了"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USBストレージ内のデータを消去中..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SDカード内のデータを消去中..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"共有ストレージを消去しています…"</string>
<string name="share" msgid="1778686618230011964">"共有"</string>
<string name="find" msgid="4808270900322985960">"検索"</string>
<string name="websearch" msgid="4337157977400211589">"ウェブ検索"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS リクエストは USSD リクエストに変更されました"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"新しい SS リクエストに変更されました"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"仕事用プロファイル"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"アラートとして送信済み"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"展開"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"折りたたむ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"展開の切り替え"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index fdcfc0c..3be3093 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi დარეკვა | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi დარეკვა"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WiFi დარეკვა"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"გამორთული"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"სასურველია Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"უპირატესობა მიენიჭოს მობილურს"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"აპი შეძლებს მდებარეობების გაცნობას თქვენი მედიაკოლექციიდან."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"აპლიკაცია <xliff:g id="APP">%s</xliff:g> ითხოვს ავტორიზაციას."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ბიომეტრიული აპარატურა მიუწვდომელია"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"არ არის ამოცნობილი"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"აღმოჩენილია თითის ნაწილობრივი ანაბეჭდი. გთხოვთ, სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"თითის ანაბეჭდი ვერ მუშავდება. გთხოვთ, სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"თითის ანაბეჭდის სენსორი დაბინძურებულია. გთხოვთ, გაასუფთაოთ და სცადოთ ხელახლა."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"თითის აღება მეტისმეტად ნელა მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"არ არის ამოცნობილი"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"თითის ანაბეჭდი ავტორიზებულია"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"სახე ავტორიზებულია"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"სახე ავტორიზებულია, დააჭირეთ დადასტურებას"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"აპს შეეძლება, შეცვალოს ანგარიშის სინქრონიზაციის პარამეტრები. მაგალითად, მისი გამოყენება შეიძლება ანგარიშის People აპთან სინქრონიზაციის ჩასართავად."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"სინქრონიზაციის სტატისტიკების წაკითხვა"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"აპს შეეძლება ანგარიშის სინქრონიზაციის სტატისტიკის, მათ შორის სინქრონიზაციის მოვლენების ისტორიისა და სინქრონიზაციისას გადაცემული მონაცემების რაოდენობის წაკითხვა."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"თქვენი USB მეხსიერების კონტენტის წაკითხვა"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"თქვენი SD ბარათის კონტენტის წაკითხვა"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"აპი წაიკითხავს USB კონტენტს."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"აპი წაიკითხავს SD ბარათის კონტენტს."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"თქვენი USB მეხსიერების კონტენტის შეცვლა ან წაშლა"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"თქვენი SD ბარათის კონტენტის შეცვლა ან წაშლა"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"უფლებას აძლევს აპს, ჩაწეროს USB მეხსიერებაზე."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"უფლებას აძლევს აპს, ჩაწეროს SD ბარათზე."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"თქვენი ზიარი მეხსიერების შიგთავსის წაკითხვა"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"საშუალებას აძლევს აპს, წაიკითხოს თქვენი ზიარი მეხსიერების შიგთავსი."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"თქვენი ზიარი მეხსიერების შიგთავსის შეცვლა ან წაშლა"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"საშუალებას აძლევს აპს, ჩაწეროს თქვენი ზიარი მეხსიერების შიგთავსი."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP ზარების წამოწყება/მიღება"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ნებას რთავს აპს განახორციელოს და მიიღოს SIP ზარები."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ტელეკომის ახალი SIM კავშირების რეგისტრაცია"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"მოვლენის არჩეული დროისთვის დაგეგმვა"</string>
<string name="view_flight" msgid="7691640491425680214">"ჩანაწერი"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"არჩეული ფრენისთვის თვალის მიდევნება"</string>
+ <string name="translate" msgid="9218619809342576858">"თარგმნა"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"არჩეული ტექსტის თარგმნა"</string>
+ <string name="define" msgid="7394820043869954211">"განსაზღვრა"</string>
+ <string name="define_desc" msgid="7910883642444919726">"არჩეული ტექსტის განსაზღვრა"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"თავისუფალი ადგილი იწურება"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 დამთხვევა</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"დასრულდა"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"მიმდინარეობს USB მეხსიერების გასუფთავება…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD ბარათის წაშლა..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"მიმდინარეობს ზიარი მეხსიერების ამოშლა…"</string>
<string name="share" msgid="1778686618230011964">"გაზიარება"</string>
<string name="find" msgid="4808270900322985960">"ძიება"</string>
<string name="websearch" msgid="4337157977400211589">"ვებ-ძიება"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS მოთხოვნა შეიცვალა USSD მოთხოვნით"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"შეიცვალა ახალი SS მოთხოვნით"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"სამსახურის პროფილი"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"გაფრთხილებით"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"გაშლა"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ჩაკეცვა"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"გაშლის გადართვა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 7f3e611..0b4ff1c 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi қоңыраулары | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi қоңыраулары"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WiFi қоңыраулары"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Өшірулі"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Қалаулы Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Таңдаулы мобильдік байланыс"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Қолданбаға медиамазмұн жинағынан геодеректерді оқуға мүмкіндік береді."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> қолданбасына аутентификация қажет."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрикалық жабдық жоқ"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Танылмады"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Саусақ ізі ішінара анықталды. Әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Саусақ ізі сенсоры лас. Тазалап, әрекетті қайталаңыз."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Саусағыңызды тым баяу қозғалттыңыз. Әрекетті қайталап көріңіз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Танылмады"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Саусақ ізі аутентификацияланды"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Бет танылды"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Бет танылды, \"Растау\" түймесін басыңыз"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Қолданбаға есептік жазбаның синхрондау параметрлерін жөндеу мүмкіндігін береді. Мысалы, бұл People қолданбасын есептік жазбамен синхрондауды қосу үшін қолданылуы мүмкін."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"үйлестіру санақтық ақпаратын оқу"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Қолданбаға есептік жазбаның синхрондалу статистикаларын, оның ішінде синхрондау шараларының тарихы және қанша дерек синхрондалғаны жайлы, оқу мүмкіндігін береді."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"Өзіңіздің USB жадыңыздың мазмұнын оқу"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"Өзіңіздің SD картаңыздың мазмұнын оқу"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Қолданбаға USB жадыңыздың мазмұнын оқу мүмкіндігін береді."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Қолданбаға SD картаңыздың мазмұнын оқу мүмкіндігін береді."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB жадыңыздың мазмұнын өзгерту немесе жою"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD картаңыздың мазмұнын өзгерту немесе жою"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Қолданбаға USB қоймасына жазуға рұқсат береді."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Қолданбаға SD картасына жазуға рұқсат береді."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ортақ жадтың мазмұнын оқу"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Қолданбаға ортақ жадтың мазмұнын оқуға мүмкіндік береді."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ортақ жадтың мазмұнын өзгерту немесе жою"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Қолданбаға ортақ жадтың мазмұнын жазуға мүмкіндік береді."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP қоңырауларын шалу/қабылдау"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Қолданбаға SIP қоңырауларын шалуға және қабылдауға рұқсат етеді."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"жаңа телекоммуникациялық SIM байланыстарын тіркеу"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Таңдалған уақытқа іс-шараны жоспарлау"</string>
<string name="view_flight" msgid="7691640491425680214">"Бақылау"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Таңдалған ұшу рейсін қадағалау"</string>
+ <string name="translate" msgid="9218619809342576858">"Аудару"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Таңдалған мәтінді аудару"</string>
+ <string name="define" msgid="7394820043869954211">"Анықтау"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Таңдалған мәтінді анықтау"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Жадта орын азайып барады"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">1 сәйкестік</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Дайын"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB жадын өшіруде…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD картаны өшіруде…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Ортақ жад тазартылуда…"</string>
<string name="share" msgid="1778686618230011964">"Бөлісу"</string>
<string name="find" msgid="4808270900322985960">"Табу"</string>
<string name="websearch" msgid="4337157977400211589">"Веб іздеу"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS сұрауы USSD сұрауына өзгертілді"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Жаңа SS сұрауына өзгертілді"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Жұмыс профилі"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Ескертілді"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Жаю"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Жию"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"жаю/жию"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 06e6540..cd2af7b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"ការហៅតាម WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"ការហៅតាម Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"ការហៅតាម Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"បិទ"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi ជាអាទិភាព"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ទិន្នន័យទូរសព្ទចល័តជាអាទិភាព"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"អនុញ្ញាតឱ្យកម្មវិធីអានទីតាំងពីបណ្ដុំមេឌៀរបស់អ្នក។"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"កម្មវិធី <xliff:g id="APP">%s</xliff:g> ចង់ធ្វើការផ្ទៀងផ្ទាត់។"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"មិនអាចប្រើឧបករណ៍ស្កេនស្នាមម្រាមដៃបានទេ"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"មិនអាចសម្គាល់បានទេ"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"បានផ្តិតយកស្នាមម្រាមដៃមិនពេញលក្ខណៈ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ឧបករណ៍ផ្តិតម្រាមដៃប្រលាក់ហើយ។ សូមសម្អាត ហើយព្យាយាមម្តងទៀត។"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ចលនាម្រាមដៃយឺតពេកហើយ។ សូមព្យាយាមម្តងទៀត។"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"មិនអាចសម្គាល់បានទេ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"បានផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"បានផ្ទៀងផ្ទាត់មុខ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"បានផ្ទៀងផ្ទាត់មុខ សូមចុចបញ្ជាក់"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ឲ្យកម្មវិធីកែការកំណត់ធ្វើសមកាលកម្មសម្រាប់គណនី។ ឧទាហរណ៍ វាអាចត្រូវបានប្រើដើម្បីបើកការធ្វើសមកាលកម្មកម្មវិធីរបស់មនុស្សជាមួយគណនី។"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"អានស្ថិតិធ្វើសមកាលកម្ម"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ឲ្យកម្មវិធីអានស្ថានភាពធ្វើសមកាលកម្មសម្រាប់គណនី រួមមានព្រឹត្តិការណ៍ប្រវត្តិធ្វើសមកាលកម្ម និងទំហំទិន្នន័យបានធ្វើសមកាលកម្ម។"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"អានមាតិកាឧបករណ៍ផ្ទុកយូអេសប៊ីរបស់អ្នក"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"អានមាតិកាកាតអេសឌីរបស់អ្នក"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"ឲ្យកម្មវិធីអានមាតិកាឧបករណ៍ផ្ទុកយូអេសប៊ីរបស់អ្នក។"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"អនុញ្ញាតឱ្យកម្មវិធីដើម្បីអានមាតិកាកាតអេសឌីរបស់អ្នក។"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"កែ ឬលុបមាតិកានៃឧបករណ៍ផ្ទុកយូអេសប៊ីរបស់អ្នក"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"កែ ឬលុបមាតិកាកាតអេសឌី"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ឲ្យកម្មវិធីសរសេរឧបករណ៍ផ្ទុកយូអេសប៊ី។"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"ឲ្យកម្មវិធីសរសេរទៅកាតអេសឌី។"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"អានខ្លឹមសារនៃទំហំផ្ទុករួមរបស់អ្នក"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"អនុញ្ញាតឱ្យកម្មវិធីអានខ្លឹមសារនៃទំហំផ្ទុករួមរបស់អ្នក។"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"កែប្រែ ឬលុបខ្លឹមសារនៃទំហំផ្ទុករួមរបស់អ្នក"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"អនុញ្ញាតឱ្យកម្មវិធីសរសេរខ្លឹមសារនៃទំហំផ្ទុករួមរបស់អ្នក។"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"បង្កើត/ទទួល ការហៅ SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ឲ្យកម្មវិធី បង្កើត និងទទួលការហៅ SIP ។"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ចុះឈ្មោះការភ្ជាប់ស៊ីមទូរគមនាគមន៍ថ្មី"</string>
@@ -1101,10 +1097,14 @@
<string name="add_contact_desc" msgid="4830217847004590345">"បញ្ចូលទៅក្នុងទំនាក់ទំនង"</string>
<string name="view_calendar" msgid="979609872939597838">"មើល"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"មើលពេលវេលាដែលបានជ្រើសរើសនៅក្នុងប្រតិទិន"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"កាលវិភាគ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"កំណត់កាលវិភាគ"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"កំណត់កាលវិភាគព្រឹត្តិការណ៍សម្រាប់ពេលវេលាដែលបានជ្រើសរើស"</string>
<string name="view_flight" msgid="7691640491425680214">"តាមដាន"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"តាមដានជើងហោះហើរដែលបានជ្រើសរើស"</string>
+ <string name="translate" msgid="9218619809342576858">"បកប្រែ"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"បកប្រែអត្ថបទដែលបានជ្រើសរើស"</string>
+ <string name="define" msgid="7394820043869954211">"កំណត់អត្ថន័យ"</string>
+ <string name="define_desc" msgid="7910883642444919726">"កំណត់អត្ថន័យពាក្យដែលបានជ្រើសរើស"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"មិនមានទំហំផ្ទុកគ្រប់គ្រាន់សម្រាប់ប្រព័ន្ធ។ សូមប្រាកដថាអ្នកមានទំហំទំនេរ 250MB ហើយចាប់ផ្ដើមឡើងវិញ។"</string>
@@ -1462,8 +1462,7 @@
<item quantity="one">ការប្រកួត 1</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"រួចរាល់"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"កំពុងលុបឧបករណ៍ផ្ទុកយូអេសប៊ី..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"កំពុងលុបកាតអេសឌី..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"កំពុងលុបទំហំផ្ទុករួម…"</string>
<string name="share" msgid="1778686618230011964">"ចែករំលែក"</string>
<string name="find" msgid="4808270900322985960">"រក"</string>
<string name="websearch" msgid="4337157977400211589">"ស្វែងរកតាមបណ្ដាញ"</string>
@@ -1835,8 +1834,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"សំណើ SS ត្រូវបានប្ដូរទៅសំណើ USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"បានប្ដូរទៅសំណើ SS ថ្មី"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ប្រវត្តិរូបការងារ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"បានជូនដំណឹង"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ពង្រីក"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"លាក់"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"បិទ/បើកការពង្រីក"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 88e7ce1..44010d2 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> ವೈ-ಫೈ"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"ವೈಫೈ ಕರೆಮಾಡುವಿಕೆ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"ವೈ-ಫೈ"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"ವೈಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ಆಫ್"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"ವೈ-ಫೈಗೆ ಆದ್ಯತೆ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ಮೊಬೈಲ್ಗೆ ಆದ್ಯತೆ"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್ಗೆ ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಬೆರಳಚ್ಚು ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಬೆರಳಚ್ಚು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಬೆರಳಚ್ಚು ಸೆನ್ಸಾರ್ ಕೊಳೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ಬೆರಳನ್ನು ತುಂಬಾ ನಿಧಾನವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ಖಾತೆಗೆ ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಖಾತೆಯನ್ನು ಹೊಂದಿರುವ ವ್ಯಕ್ತಿಗಳ ಸಿಂಕ್ ಸಕ್ರಿಯಗೊಳಿಸಲು ಇದನ್ನು ಬಳಸಬಹುದಾಗಿದೆ."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ಸಿಂಕ್ ಅಂಕಿಅಂಶಗಳನ್ನು ಓದಿರಿ"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ಸಿಂಕ್ ಈವೆಂಟ್ಗಳ ಇತಿಹಾಸ ಮತ್ತು ಎಷ್ಟು ಪ್ರಮಾಣದ ಡೇಟಾವನ್ನು ಸಿಂಕ್ ಮಾಡಲಾಗಿದೆ ಎಂಬುದು ಸೇರಿದಂತೆ, ಒಂದು ಖಾತೆಗಾಗಿ ಸಿಂಕ್ ಅಂಕಿಅಂಶಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ನಿಮ್ಮ USB ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಓದಿ"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ನಿಮ್ಮ SD ಕಾರ್ಡ್ನ ವಿಷಯಗಳನ್ನು ಓದಿ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"ನಿಮ್ಮ USB ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"ನಿಮ್ಮ SD ಕಾರ್ಡ್ನ ವಿಷಯಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ನಿಮ್ಮ USB ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಮಾರ್ಪಡಿಸಿ ಅಥವಾ ಅಳಿಸಿ"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ನಿಮ್ಮ SD ಕಾರ್ಡ್ನ ವಿಷಯಗಳನ್ನು ಮಾರ್ಪಡಿಸಿ ಅಥವಾ ಅಳಿಸಿ"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB ಸಂಗ್ರಹಣೆಗೆ ಬರೆಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SD ಕಾರ್ಡ್ಗೆ ಬರೆಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಓದಿ"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಓದಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಮಾರ್ಪಡಿಸಿ ಅಥವಾ ಅಳಿಸಿ"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಬರೆಯಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"ಎಸ್ಐಪಿ ಕರೆಗಳನ್ನು ಮಾಡಿ/ಸ್ವೀಕರಿಸಿ"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ಎಸ್ಐಪಿ ಕರೆಗಳನ್ನು ಮಾಡಲು ಮತ್ತು ಸ್ವೀಕರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ಹೊಸ ಟೆಲಿಕಾಮ್ ಸಿಮ್ ಸಂಪರ್ಕಗಳನ್ನು ನೋಂದಾಯಿಸಿ"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ಆಯ್ಕೆ ಮಾಡಿದ ಸಮಯಕ್ಕೆ ಈವೆಂಟ್ ನಿಗದಿಪಡಿಸಿ"</string>
<string name="view_flight" msgid="7691640491425680214">"ಟ್ರ್ಯಾಕ್"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ಆಯ್ಕೆಮಾಡಿದ ವಿಮಾನವನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಿ"</string>
+ <string name="translate" msgid="9218619809342576858">"ಅನುವಾದ"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ಆಯ್ಕೆಮಾಡಿದ ಪಠ್ಯವನ್ನು ಅನುವಾದಿಸಿ"</string>
+ <string name="define" msgid="7394820043869954211">"ವಿವರಿಸಿ"</string>
+ <string name="define_desc" msgid="7910883642444919726">"ಆಯ್ಕೆಮಾಡಿದ ಪಠ್ಯವನ್ನು ವಿವರಿಸಿ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g> ರಲ್ಲಿ <xliff:g id="INDEX">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"ಮುಗಿದಿದೆ"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB ಸಂಗ್ರಹಣೆ ಅಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD ಕಾರ್ಡ್ ಅಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"ಹಂಚಲಾದ ಸಂಗ್ರಹಣೆಯನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="share" msgid="1778686618230011964">"ಹಂಚು"</string>
<string name="find" msgid="4808270900322985960">"ಹುಡುಕಿ"</string>
<string name="websearch" msgid="4337157977400211589">"ವೆಬ್ ಹುಡುಕಾಟ"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS ವಿನಂತಿಯನ್ನು USSD ವಿನಂತಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"ಹೊಸ SS ವಿನಂತಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"ಎಚ್ಚರಿಕೆ ನೀಡಲಾಗಿದೆ"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ಕುಗ್ಗಿಸಿ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ವಿಸ್ತರಣೆ ಟಾಗಲ್ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 97d799e..bdc50bf 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -70,7 +70,7 @@
<string name="ThreeWCMmi" msgid="9051047170321190368">"3자 통화"</string>
<string name="RuacMmi" msgid="7827887459138308886">"원하지 않는 통화 수신 거부"</string>
<string name="CndMmi" msgid="3116446237081575808">"통화 번호 전달"</string>
- <string name="DndMmi" msgid="1265478932418334331">"응답 거부"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"방해 금지 모드"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"발신자 번호가 기본적으로 제한됨으로 설정됩니다. 다음 통화: 제한됨"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"발신자 번호가 기본적으로 제한됨으로 설정됩니다. 다음 통화: 제한되지 않음"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한됨"</string>
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi 통화 | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi 통화"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi 통화"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"꺼짐"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi를 기본으로 설정"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"모바일에 최적화됨"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"앱에서 미디어 컬렉션의 위치를 읽도록 허용합니다."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> 애플리케이션에서 인증을 요청합니다"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"생체 인식 하드웨어를 사용할 수 없음"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"인식할 수 없음"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"지문이 일부만 인식되었습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"지문 센서를 깨끗이 닦고 다시 시도하세요."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"손가락을 너무 느리게 움직였습니다. 다시 시도해 주세요."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"인식할 수 없음"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"지문이 인증됨"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"얼굴이 인증되었습니다"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"얼굴이 인증되었습니다. 확인을 누르세요"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"앱이 계정의 동기화 설정을 수정할 수 있도록 허용합니다. 예를 들어, 계정에서 주소록 앱을 동기화할 목적으로 앱이 사용될 수 있습니다."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"동기화 통계 읽기"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"앱이 동기화된 일정의 기록이나 동기화된 데이터의 양 등을 포함하여 계정의 동기화 통계를 읽을 수 있도록 허용합니다."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB 저장소의 콘텐츠 읽기"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD 카드의 콘텐츠 읽기"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"앱이 USB 저장소의 콘텐츠를 읽을 수 있도록 허용합니다."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"앱이 SD 카드의 콘텐츠를 읽을 수 있도록 허용합니다."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB 저장소의 콘텐츠 수정 또는 삭제"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD 카드의 콘텐츠 수정 또는 삭제"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"앱이 USB 저장소에 쓸 수 있도록 허용합니다."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"앱이 SD 카드에 쓸 수 있도록 허용합니다."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"공유 저장공간의 콘텐츠 읽기"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"앱이 공유 저장공간의 콘텐츠를 읽도록 허용합니다."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"공유 저장공간의 콘텐츠 수정 또는 삭제"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"앱이 공유 저장공간의 콘텐츠에 쓰도록 허용합니다."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP 통화 발신/수신"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"앱에서 SIP 통화를 발신 및 수신하도록 허용"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"새로운 통신 SIM 연결 등록"</string>
@@ -648,8 +644,8 @@
<string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"보유자가 이동통신사 메시지 서비스의 최상위 인터페이스에 고정할 수 있습니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindCarrierServices" msgid="3233108656245526783">"이동통신사 서비스 사용"</string>
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"권한을 가진 애플리케이션에서 이동통신사 서비스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
- <string name="permlab_access_notification_policy" msgid="4247510821662059671">"알림 일시중지에 접근"</string>
- <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"앱에 알림 일시중지 설정을 읽고 작성하도록 허용합니다."</string>
+ <string name="permlab_access_notification_policy" msgid="4247510821662059671">"방해 금지 모드에 접근"</string>
+ <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"앱에서 방해 금지 모드 설정을 읽고 작성하도록 허용합니다."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"비밀번호 규칙 설정"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"화면 잠금 비밀번호와 PIN에 허용되는 길이와 문자 수를 제어합니다."</string>
<string name="policylab_watchLogin" msgid="5091404125971980158">"화면 잠금 해제 시도 모니터링"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"선택한 시간의 일정 예약"</string>
<string name="view_flight" msgid="7691640491425680214">"추적"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"선택한 항공편 추적"</string>
+ <string name="translate" msgid="9218619809342576858">"번역"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"선택한 텍스트 번역"</string>
+ <string name="define" msgid="7394820043869954211">"정의"</string>
+ <string name="define_desc" msgid="7910883642444919726">"선택한 텍스트 정의"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">검색결과 1개</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"완료"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB 저장소 지우는 중..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD 카드 지우는 중..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"공유 저장공간 지우는 중…"</string>
<string name="share" msgid="1778686618230011964">"공유"</string>
<string name="find" msgid="4808270900322985960">"찾기"</string>
<string name="websearch" msgid="4337157977400211589">"웹 검색"</string>
@@ -1772,8 +1771,8 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"관리자에 의해 설치되었습니다."</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"관리자에 의해 업데이트되었습니다."</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"관리자에 의해 삭제되었습니다."</string>
- <string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"배터리 세이버를 사용하면 배터리 수명을 늘리기 위해 기기의 일부 기능이 사용 중지되며 앱이 제한됩니다. "<annotation id="url">"자세히 알아보기"</annotation></string>
- <string name="battery_saver_description" msgid="769989536172631582">"배터리 세이버를 사용하면 배터리 수명을 늘리기 위해 기기의 일부 기능이 사용 중지되며 앱이 제한됩니다."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"절전 모드를 사용하면 배터리 수명을 늘리기 위해 기기의 일부 기능이 사용 중지되며 앱이 제한됩니다. "<annotation id="url">"자세히 알아보기"</annotation></string>
+ <string name="battery_saver_description" msgid="769989536172631582">"절전 모드를 사용하면 배터리 수명을 늘리기 위해 기기의 일부 기능이 사용 중지되며 앱이 제한됩니다."</string>
<string name="data_saver_description" msgid="6015391409098303235">"데이터 사용량을 줄이기 위해 데이터 절약 모드는 일부 앱이 백그라운드에서 데이터를 전송하거나 수신하지 못하도록 합니다. 현재 사용 중인 앱에서 데이터에 액세스할 수 있지만 빈도가 줄어듭니다. 예를 들면, 이미지를 탭하기 전에는 이미지가 표시되지 않습니다."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"데이터 절약 모드를 사용할까요?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"사용 설정"</string>
@@ -1812,10 +1811,10 @@
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>까지"</string>
<string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(다음 알람)까지"</string>
<string name="zen_mode_forever" msgid="931849471004038757">"사용 중지할 때까지"</string>
- <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"알림 일시중지 기능을 사용 중지할 때까지"</string>
+ <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"방해 금지 모드를 사용 중지할 때까지"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="2821479483960330739">"접기"</string>
- <string name="zen_mode_feature_name" msgid="5254089399895895004">"알림 일시중지"</string>
+ <string name="zen_mode_feature_name" msgid="5254089399895895004">"방해 금지 모드"</string>
<string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"다운타임"</string>
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"평일 밤"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"주말"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS 요청이 USSD 요청으로 변경됨"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"새 SS 요청으로 변경됨"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"직장 프로필"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"알림 전송됨"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"펼치기"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"접기"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"확장 전환"</string>
@@ -1958,10 +1956,10 @@
<string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"전화 및 알림이 오면 진동이 사용됩니다."</string>
<string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"전화 및 알림 소리가 음소거됩니다."</string>
<string name="notification_channel_system_changes" msgid="5072715579030948646">"시스템 변경사항"</string>
- <string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"알림 일시중지"</string>
- <string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"새로운 기능: 알림 일시중지 기능으로 알림 숨기기"</string>
+ <string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"방해 금지 모드"</string>
+ <string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"새로운 기능: 방해 금지 모드로 알림 숨기기"</string>
<string name="zen_upgrade_notification_visd_content" msgid="5533674060311631165">"자세히 알아보고 변경하려면 탭하세요."</string>
- <string name="zen_upgrade_notification_title" msgid="3799603322910377294">"알림 일시중지 변경"</string>
+ <string name="zen_upgrade_notification_title" msgid="3799603322910377294">"방해 금지 모드 변경"</string>
<string name="zen_upgrade_notification_content" msgid="1794994264692424562">"차단된 항목을 확인하려면 탭하세요."</string>
<string name="notification_app_name_system" msgid="4205032194610042794">"시스템"</string>
<string name="notification_app_name_settings" msgid="7751445616365753381">"설정"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 6608029..f6e9b72 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi аркылуу чалуу | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi аркылуу чалуу"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi‑Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi аркылуу чалынууда"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Өчүк"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi тандалган"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Тандалган мобилдик түзмөк"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> колдонмосунда аутентификациядан өтүңүз."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрикалык аппарат жеткиликсиз"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Таанылган жок"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Манжа изи жарым-жартылай аныкталды. Кайра аракет кылыңыз."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Манжа изи иштелбей койду. Кайра аракет кылыңыз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Манжа изинин сенсору кирдеп калган. Тазалап, кайра аракет кылыңыз."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Манжа өтө жай жылды. Кайра аракет кылыңыз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Таанылган жок"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Манжа изинин аныктыгы текшерилди"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Жүздүн аныктыгы текшерилди"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Колдонмого эсеп менен синхрондошуу тууралоолорун өзгөртүү уруксатын берет. Мисалы, бул Кишилер колдонмосун эсеп менен синхрондошуусун иштете алат."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"шайкештирүү статистикасын окуу"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Колдонмого эсептин статистикасын, синхрондоштуруу тарыхын, анын ичинде, канча берилиштер синхрондошкондугун окуганга уруксат берет."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB сактагычыңыздын мазмунун окуу"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD-картаңыздын мазмунун окуу"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Колдонмого USB сактагычыңыздын мазмунун окуу мүмкүнчүлүгүн берет."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Колдонмого SD-картаңыздын мазмунун окуу мүмкүнчүлүгүн берет."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB сактагычыңыздын мазмунун өзгөртүү же жок кылуу"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD-картаңыздын мазмунун өзгөртүү же жок кылуу"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Колдонмого USB сактагычына жазуу мүмкүнчүлүгүн берет."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Колдонмого SD картага жазуу мүмкүнчүлүгүн берет."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"жалпы сактагычыңыздын мазмунун окуу"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Колдонмого жалпы сактагычыңыздын мазмунун окуу мүмкүнчүлүгүн берет."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"жалпы сактагычыңыздын мазмунун өзгөртүү же жок кылуу"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Колдонмого жалпы сактагычыңыздын мазмунун жазуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP чалуу/чалууну кабыл алуу"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Колдонмого SIP чалууларга жана чалууларды кабыл алууга мүмкүнчүлүк берет."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"жаңы телеком SIM туташууларын каттоо"</string>
@@ -1087,15 +1083,15 @@
<string name="deleteText" msgid="6979668428458199034">"Жок кылуу"</string>
<string name="inputMethod" msgid="1653630062304567879">"Киргизүү ыкмасы"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Текст боюнча иштер"</string>
- <string name="email" msgid="4560673117055050403">"Электрондук почта"</string>
+ <string name="email" msgid="4560673117055050403">"Кат жөнөтүү"</string>
<string name="email_desc" msgid="3638665569546416795">"Тандалган дарекке электрондук кат жөнөтүү"</string>
<string name="dial" msgid="1253998302767701559">"Чалуу"</string>
<string name="dial_desc" msgid="6573723404985517250">"Тандалган телефон номерине чалуу"</string>
- <string name="map" msgid="5441053548030107189">"Карта"</string>
+ <string name="map" msgid="5441053548030107189">"Картадан кароо"</string>
<string name="map_desc" msgid="1836995341943772348">"Тандалган даректи картада табуу"</string>
<string name="browse" msgid="1245903488306147205">"Ачуу"</string>
<string name="browse_desc" msgid="8220976549618935044">"Тандалган URL\'ди ачуу"</string>
- <string name="sms" msgid="4560537514610063430">"Билдирүү"</string>
+ <string name="sms" msgid="4560537514610063430">"Билдирүү жазуу"</string>
<string name="sms_desc" msgid="7526588350969638809">"Тандалган телефон номерине билдирүү жөнөтүү"</string>
<string name="add_contact" msgid="7867066569670597203">"Кошуу"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Байланыштарга кошуу"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Иш-чараны тандалган убакытка графикке киргизүү"</string>
<string name="view_flight" msgid="7691640491425680214">"Көз салуу"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Тандалган аба каттамына көз салуу"</string>
+ <string name="translate" msgid="9218619809342576858">"Которуу"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Тандалган текстти которуу"</string>
+ <string name="define" msgid="7394820043869954211">"Аныктоо"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Тандалган текстти аныктоо"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сактагычта орун калбай баратат"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
@@ -1462,8 +1462,7 @@
<item quantity="one">1 дал келүү</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Даяр"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB сактагыч тазаланууда…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD-карта тазаланууда…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Жалпы сактагыч тазаланууда…"</string>
<string name="share" msgid="1778686618230011964">"Бөлүшүү"</string>
<string name="find" msgid="4808270900322985960">"Табуу"</string>
<string name="websearch" msgid="4337157977400211589">"Интернеттен издөө"</string>
@@ -1835,8 +1834,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS сурамы USSD сурамына өзгөртүлдү"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Жаңы SS сурамына өзгөртүлдү"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Жумуш профили"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Эскертилди"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Жайып көрсөтүү"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Жыйыштыруу"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"жайып көрсөтүү же жыйыштыруу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index e12ec28..e925d92 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi Calling | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"ການໂທ Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"ການໂທ Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ປິດ"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"ເລືອກໃຊ້ Wi-Fi ກ່ອນ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ຕ້ອງການໃຊ້ມືຖື"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ອະນຸຍາດໃຫ້ແອັບອ່ານສະຖານທີ່ຈາກຄໍເລັກຊັນມີເດຍຂອງທ່ານ."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"ແອັບພລິເຄຊັນ <xliff:g id="APP">%s</xliff:g> ຕ້ອງການກວດຮັບຮອງຄວາມຖືກຕ້ອງ."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ຮາດແວຊີວະມິຕິບໍ່ສາມາດໃຊ້ໄດ້"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ບໍ່ຮັບຮູ້"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ກວດພົບລາຍນີ້ວມືບາງສ່ວນແລ້ວ. ກະລຸນາລອງໃໝ່ອີກ."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ບໍ່ສາມາດດຳເນີນການລາຍນີ້ວມືໄດ້. ກະລຸນາລອງໃໝ່ອີກ."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ເຊັນເຊີລາຍນີ້ວມືເປື້ອນ. ກະລຸນາທຳຄວາມສະອາດ ແລະລອງໃໝ່ອີກ."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ຍ້າຍນີ້ວມືໄປຊ້າເກີນໄປ. ກະລຸນາລອງໃໝ່ອີກ."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"ບໍ່ຮັບຮູ້"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ, ກະລຸນາກົດຢືນຢັນ"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂການຕັ້ງຄ່າການຊິ້ງຂໍ້ມູນສຳລັບບັນຊີ. ຍົກຕົວຢ່າງ: ມັນສາມາດໃຊ້ເພື່ອເປີດນຳໃຊ້ການຊິ້ງຂໍ້ມູນຂອງ People ແອັບຯກັບບັນຊີໃດນຶ່ງໄດ້."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ອ່ານສະຖິຕິການຊິ້ງຂໍ້ມູນ"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ອະນຸຍາດໃຫ້ແອັບຯ ອ່ານສະຖິຕິການຊິ້ງຂໍ້ມູນຂອງບັນຊີໃດນຶ່ງ ຮວມທັງປະຫວັດການຊິ້ງຂໍ້ມູນ ແລະຈຳນວນຂໍ້ມູນທີ່ຖືກຊິ້ງ."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ອ່ານເນື້ອຫາຕ່າງໆໃນບ່ອນຈັດເກັບຂໍ້ມູນ USB ຂອງທ່ານ"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ອ່ານເນື້ອຫາຕ່າງໆໃນ SD Card ຂອງທ່ານ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"ອະນຸຍາດໃຫ້ແອັບຯອ່ານເນື້ອຫາຕ່າງໆໃນບ່ອນຈັດເກັບຂໍ້ມູນ USB ຂອງທ່ານ."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"ອະນຸຍາດໃຫ້ແອັບຯອ່ານເນື້ອຫາຕ່າງໆໃນ SD Card ຂອງທ່ານ."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ແກ້ໄຂ ຫຼືລຶບເນື້ອຫາໃນບ່ອນຈັດເກັບຂໍ້ມູນ USB ຂອງທ່ານ"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ແກ້ໄຂ ຫຼືລຶບເນື້ອຫາຂອງ SD card ຂອງທ່ານ"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ອະນຸຍາດໃຫ້ແອັບຯຂຽນຂໍ້ມູນໃສ່ບ່ອນຈັດເກັບຂໍ້ມູນ USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"ອະນຸຍາດໃຫ້ແອັບຯຂຽນຂໍ້ມູນລົງໃນ SD card ໄດ້."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ອ່ານເນື້ອຫາຕ່າງໆໃນບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"ອະນຸຍາດໃຫ້ແອັບອ່ານເນື້ອຫາຕ່າງໆໃນບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ແກ້ໄຂ ຫຼືລຶບເນື້ອຫາໃນບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"ອະນຸຍາດໃຫ້ແອັບຂຽນເນື້ອຫາຕ່າງໆຂອງບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"ຮັບສາຍ/ໂທອອກ ຜ່ານ SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ອະນຸຍາດໃຫ້ແອັບຯສາມາດຮັບສາຍ ແລະໂທອອກຜ່ານ SIP ໄດ້"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ລົງທະບຽນ SIM ການເຊື່ອມຕໍ່ໂທລະຄົມມະນາຄົມໃໝ່"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ກຳນົດເວລາສຳລັບເວລາທີ່ເລືອກ"</string>
<string name="view_flight" msgid="7691640491425680214">"ແທຣັກ"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ຕິດຕາມຖ້ຽວບິນທີ່ເລືອກ"</string>
+ <string name="translate" msgid="9218619809342576858">"ແປພາສາ"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ແປຂໍ້ຄວາມທີ່ເລືອກ"</string>
+ <string name="define" msgid="7394820043869954211">"ນິຍາມ"</string>
+ <string name="define_desc" msgid="7910883642444919726">"ນິຍາມຂໍ້ຄວາມທີ່ເລືອກໄວ້"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ບໍ່ມີບ່ອນເກັບຂໍ້ມູນພຽງພໍສຳລັບລະບົບ. ກວດສອບໃຫ້ແນ່ໃຈວ່າທ່ານມີພື້ນທີ່ຫວ່າງຢ່າງໜ້ອຍ 250MB ແລ້ວລອງໃໝ່."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 ກົງກັນ</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"ແລ້ວໆ"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"ກຳລັງລຶບ ບ່ອນຈັດເກັບຂໍ້ມູນ USB …"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"ກຳລັງລຶບ SD card..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"ກຳລັງລຶບບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນ…"</string>
<string name="share" msgid="1778686618230011964">"ແບ່ງປັນ"</string>
<string name="find" msgid="4808270900322985960">"ຊອກຫາ"</string>
<string name="websearch" msgid="4337157977400211589">"ຊອກຫາເວັບ"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"ປ່ຽນຄຳຂໍ SS ເປັນຄຳຂໍ USSD ແລ້ວ"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"ປ່ຽນຄຳຂໍ SS ໃໝ່ແລ້ວ"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"ເຕືອນແລ້ວ"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ຂະຫຍາຍ"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ຫຍໍ້ເຂົ້າ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ປິດ/ເປີດ ການຂະຫຍາຍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 86cee5e..ada983f 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> „Wi-Fi“"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"„Wi-Fi“ skambinimas | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> „VoWifi“"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"„Wi-Fi“ skambinimas"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"„Wi-Fi“ skambinimas"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Išjungta"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Pageidautinas „Wi-Fi“ ryšys"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Pirmenybė mobiliojo ryšio tinklui"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Programai leidžiama skaityti vietoves iš medijos kolekcijos."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Programa „<xliff:g id="APP">%s</xliff:g>“ nori jus autentifikuoti."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrinė aparatinė įranga nepasiekiama"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Neatpažinta"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Aptiktas dalinis piršto antspaudas. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nepavyko apdoroti piršto antspaudo. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Piršto antspaudo jutiklis purvinas. Nuvalykite ir bandykite dar kartą."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Per lėtai judinate pirštą. Bandykite dar kartą."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Neatpažinta"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Piršto antspaudas autentifikuotas"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Veidas autentifikuotas"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Veidas autentifikuotas, paspauskite patvirtinimo mygtuką"</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Leidžiama programai keisti sinchronizuotus paskyros nustatymus. Pvz., tai gali būti naudojama norint įgalinti Žmonių programos sinchronizavimą su paskyra."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"skaityti sinchronizavimo statistiką"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Leidžiama programai skaityti sinchronizuotą paskyros statistiką, įskaitant sinchronizuotų įvykių istoriją ir informaciją, kiek duomenų sinchronizuota."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"skaityti USB atminties turinį"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"skaityti SD kortelės turinį"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Leidžiama skait. USB atmintį."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Programai leidžiama skaityti SD kortelės turinį."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"keisti / trinti USB atm. turinį"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"keisti arba trinti SD kortelės turinį"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Leidž. progr. raš. į USB atm."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Leidžiama programai rašyti į SD kortelę."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"skaityti bendr. atmint. turinį"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Pr. leidž. sk. bendr. atm. t."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"keisti / trinti bendr. atm. t."</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Pr. leidž. raš. bendr. atm. t."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"skambinti / priimti SIP skambučius"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Leidžiama programai skambinti ir priimti SIP skambučius."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registruoti naujus telekomunikacijų SIM ryšius"</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planuoti įvykį pasirinktam laikui"</string>
<string name="view_flight" msgid="7691640491425680214">"Stebėti"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Stebėti pasirinktą skrydį"</string>
+ <string name="translate" msgid="9218619809342576858">"Versti"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Versti pasirinktą tekstą"</string>
+ <string name="define" msgid="7394820043869954211">"Apibrėžti"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Apibrėžti pasirinktą tekstą"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Mažėja laisvos saugyklos vietos"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kai kurios sistemos funkcijos gali neveikti"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string>
@@ -1506,8 +1506,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> iš <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Atlikta"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Ištrinama USB atmintis..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Ištrinama SD kortelė..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Ištrinama bendrinama saugykla…"</string>
<string name="share" msgid="1778686618230011964">"Bendrinti"</string>
<string name="find" msgid="4808270900322985960">"Ieškoti"</string>
<string name="websearch" msgid="4337157977400211589">"Žiniat. paieška"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS užklausa pakeista į USSD užklausą"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Pakeista į naują SS užklausą"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Darbo profilis"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Įspėjimas išsiųstas"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Išskleisti"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Sutraukti"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"perjungti išskleidimą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 6f63f0c..dd4b125 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -137,14 +137,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi zvani | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi zvani"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi zvani"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Izslēgts"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Vēlams Wi-Fi tīkls"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Vēlams mobilo datu savienojums"</string>
@@ -536,6 +532,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ļauj lietotnei lasīt atrašanās vietas no jūsu multivides kolekcijas."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Lietotne <xliff:g id="APP">%s</xliff:g> pieprasa autentificēt."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisko datu aparatūra nav pieejama"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Dati nav atpazīti"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Noteikts daļējs pirksta nospiedums. Lūdzu, mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Pirkstu nospiedumu sensors ir netīrs. Lūdzu, notīriet to un mēģiniet vēlreiz."</string>
@@ -543,7 +544,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Pārāk lēna pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Dati nav atpazīti"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Pirksta nospiedums tika autentificēts."</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Seja autentificēta"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Seja ir autentificēta. Nospiediet pogu Apstiprināt."</string>
@@ -599,14 +599,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Ļauj lietotnei pārveidot konta sinhronizācijas iestatījumus. Piemēram, to var izmantot, lai iespējotu lietotnes Personas sinhronizēšanu ar kontu."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lasīt sinhronizācijas statistiku"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Ļauj lietotnei lasīt konta sinhronizācijas statistiku, tostarp sinhronizācijas notikumu vēsturi un sinhronizēto datu apjomu."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"lasīt USB atmiņas saturu"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lasīt SD kartes saturu"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Ļauj liet. lasīt USB atm. sat."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Ļauj lietotnei lasīt SD kartes saturu."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB krātuves satura mainīšana/dzēšana"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD kartes satura pārveidošana vai dzēšana"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Ļauj lietotnei rakstīt USB atmiņā."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ļauj lietotnei rakstīt SD kartē."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"Jūsu kopīgotās krātuves satura lasīšana"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Ļauj lietotnei lasīt jūsu kopīgotās krātuves saturu."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"Jūsu kopīgotās krātuves satura pārveidošana vai dzēšana"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Ļauj lietotnei rakstīt jūsu kopīgotās krātuves saturu."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP zvanu veikšana/saņemšana"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Ļauj lietotnei veikt un saņemt SIP zvanus."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"reģistrēt jaunus telekomunikāciju SIM savienojumus"</string>
@@ -1111,7 +1107,7 @@
<string name="email_desc" msgid="3638665569546416795">"Nosūtīt e-pasta ziņojumu uz atlasīto adresi"</string>
<string name="dial" msgid="1253998302767701559">"Zvanīt"</string>
<string name="dial_desc" msgid="6573723404985517250">"Zvanīt uz atlasīto tālruņa numuru"</string>
- <string name="map" msgid="5441053548030107189">"Maps"</string>
+ <string name="map" msgid="5441053548030107189">"Karte"</string>
<string name="map_desc" msgid="1836995341943772348">"Atrast atlasīto adresi"</string>
<string name="browse" msgid="1245903488306147205">"Atvērt"</string>
<string name="browse_desc" msgid="8220976549618935044">"Atvērt atlasīto URL"</string>
@@ -1125,6 +1121,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ieplānot pasākumu konkrētā laikā"</string>
<string name="view_flight" msgid="7691640491425680214">"Izsekot"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Izsekot atlasīto lidojumu"</string>
+ <string name="translate" msgid="9218619809342576858">"Tulkot"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Tulkot atlasīto tekstu"</string>
+ <string name="define" msgid="7394820043869954211">"Definēt"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definēt atlasīto tekstu"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string>
@@ -1483,8 +1483,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g>. no <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Gatavs"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Notiek USB atmiņas dzēšana..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Notiek SD kartes dzēšana..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Notiek koplietotās krātuves dzēšana…"</string>
<string name="share" msgid="1778686618230011964">"Kopīgot"</string>
<string name="find" msgid="4808270900322985960">"Atrast"</string>
<string name="websearch" msgid="4337157977400211589">"Meklēt tīmeklī"</string>
@@ -1866,8 +1865,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS pieprasījums mainīts uz USSD pieprasījumu"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Mainīts uz jaunu SS pieprasījumu"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Darba profils"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Brīdināts"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Izvērst"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Sakļaut"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"izvērst/sakļaut"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d8bb4a7..8b73b99 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Повици преку Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"Глас преку Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Повикување преку Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Повикување преку Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"Глас преку Wi-Fi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Исклучено"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Се претпочита Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Претпочитам мобилен интернет"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Дозволува апликацијата да чита локации од вашата збирка на аудиовизуелни содржини."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Апликацијата <xliff:g id="APP">%s</xliff:g> сака да ве провери."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрискиот хардвер е недостапен"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Непознат"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откриен е делумен отпечаток. Обидете се повторно."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатокот не можеше да се обработи. Обидете се повторно."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Прстот се движеше премногу бавно. Обидете се повторно."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Непознат"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечатокот е проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Лицето е проверено"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Лицето е проверено, притиснете го копчето „Потврди“"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Овозможува апликацијата да ги менува поставките за синхронизирање на сметка. На пример, ова може да се употреби да овозможи синхронизација на апликацијата „Луѓе“ со сметка."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"читај статистика за синхронизација"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Овозможува апликацијата да ја чита статистиката за синхронизација на сметка, вклучувајќи ја и историјата на синхронизирани настани и колку податоци се синхронизирани."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"прочитај ги содржините на твојата USB меморија"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"прочитај ги содржините на твојата СД картичка"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Овозможува апликацијата да ги чита содржините од вашето USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Овозможува апликацијата да ги чита содржините од вашата СД картичка."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"измени ги или избриши ги содржините на твојата USB меморија"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"измени ги или избриши ги содржините на твојата СД картичка"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Дозволува пишување на USB-склад."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозволува апликацијата да пишува на СД-картичката."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ги чита содржините од заедничкото место за складирање"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Дозволува апликацијата да ги чита содржините од заедничкото место за складирање."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ги менува или брише содржините на заедничкото место за складирање"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Дозволува апликацијата да ги пишува содржините на заедничкото место за складирање."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"остварува/прима повици преку SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Дозволува апликацијата да остварува и прима повици преку SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"регистрира нови телекомуникациски врски преку SIM"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Закажува настан за избраното време"</string>
<string name="view_flight" msgid="7691640491425680214">"Песна"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Следи избран лет"</string>
+ <string name="translate" msgid="9218619809342576858">"Преведи"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Преведете го избраниот текст"</string>
+ <string name="define" msgid="7394820043869954211">"Дефинирај"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Дефинирајте го избраниот текст"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Капацитетот е речиси полн"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некои системски функции може да не работат"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 МБ и рестартирајте."</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> од <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Готово"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Бришење USB меморија..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Бришење СД картичка..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Бришење споделена меморија…"</string>
<string name="share" msgid="1778686618230011964">"Сподели"</string>
<string name="find" msgid="4808270900322985960">"Пронајди"</string>
<string name="websearch" msgid="4337157977400211589">"Пребарај веб"</string>
@@ -1836,8 +1835,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Барањето SS е изменето во барање USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Променето на ново барање SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Работен профил"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Предупредено"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Прошири"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Собери"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"вклучи/исклучи проширување"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 4a5f1cd..8c451a2 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> വൈഫൈ"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"വൈഫൈ കോളിംഗ് | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> Voവൈഫൈ"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"വൈഫൈ കോളിംഗ്"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"വൈഫൈ"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"വൈഫൈ കോളിംഗ്"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"Voവൈഫൈ"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ഓഫ്"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"വൈഫൈ തിരഞ്ഞെടുത്തിരിക്കുന്നു"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"മൊബൈൽ ഡാറ്റ ഉപയോഗിക്കാൻ താൽപ്പര്യപ്പെടുന്നു"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ആപ്പിന് നിങ്ങളെ പരിശോധിച്ചുറപ്പിക്കണം."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ബയോമെട്രിക് ഹാർഡ്വെയർ ലഭ്യമല്ല"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"തിരിച്ചറിഞ്ഞില്ല"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"വിരലടയാളം ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"വിരലടയാളം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"വിരലടയാള സെൻസറിന് വൃത്തിയില്ല. അത് ശുചിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"വിരൽ വളരെ പതുക്കെ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"തിരിച്ചറിഞ്ഞില്ല"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു, സ്ഥിരീകരിക്കുക അമർത്തുക"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ഒരു അക്കൗണ്ടിനായി സമന്വയ ക്രമീകരണങ്ങൾ പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ആളുകൾ അപ്ലിക്കേഷൻ ഒരു അക്കൗണ്ടിൽ സമന്വയിപ്പിക്കുന്നത് പ്രവർത്തനക്ഷമമാക്കാൻ ഇത് ഉപയോഗിക്കാം."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"സമന്വയ സ്ഥിതിവിവരണക്കണക്കുകൾ വായിക്കുക"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"സമന്വയ ഇവന്റുകളുടെ ചരിത്രവും ഡാറ്റ എത്രത്തോളം സമന്വയിപ്പിച്ചുവെന്നതും ഉൾപ്പെടെ, ഒരു അക്കൗണ്ടിനായി സമന്വയ സ്ഥിതിവിവരക്കണക്കുകൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"നിങ്ങളുടെ USB കാർഡ് ഉള്ളടക്കം റീഡുചെയ്യുക"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"നിങ്ങളുടെ SD കാർഡ് ഉള്ളടക്കം റീഡുചെയ്യുക"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"നിങ്ങളുടെ USB സംഭരണത്തിലെ ഉള്ളടക്കങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"നിങ്ങളുടെ SD കാർഡിലെ ഉള്ളടക്കങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"നിങ്ങളുടെ USB കാർഡ് ഉള്ളടക്കങ്ങൾ പരിഷ്ക്കരിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യുക"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"നിങ്ങളുടെ SD കാർഡ് ഉള്ളടക്കങ്ങൾ പരിഷ്ക്കരിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യുക"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB സംഭരണത്തിൽ റൈറ്റുചെയ്യുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SD കാർഡിൽ റൈറ്റുചെയ്യുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"നിങ്ങൾ പങ്കിടുന്ന സ്റ്റോറേജിലെ ഉള്ളടക്കങ്ങൾ വായിക്കുക"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"നിങ്ങൾ പങ്കിടുന്ന സ്റ്റോറേജിലെ ഉള്ളടക്കങ്ങൾ വായിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"നിങ്ങൾ പങ്കിടുന്ന സ്റ്റോറേജിലെ ഉള്ളടക്കങ്ങൾ പരിഷ്ക്കരിക്കുക അല്ലെങ്കിൽ ഇല്ലാതാക്കുക"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"നിങ്ങൾ പങ്കിടുന്ന സ്റ്റോറേജിലെ ഉള്ളടക്കങ്ങൾ എഴുതാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP കോളുകൾ വിളിക്കുക/സ്വീകരിക്കുക"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP കോളുകൾ വിളിക്കാനും സ്വീകരിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"പുതിയ ടെലികോം SIM കണക്ഷനുകൾ രജിസ്റ്റർ ചെയ്യുക"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"തിരഞ്ഞെടുത്ത സമയത്തേക്ക് ഇവന്റ് ഷെഡ്യൂൾ ചെയ്യുക"</string>
<string name="view_flight" msgid="7691640491425680214">"ട്രാക്ക്"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"തിരഞ്ഞെടുത്ത ഫ്ലൈറ്റ് ട്രാക്ക് ചെയ്യുക"</string>
+ <string name="translate" msgid="9218619809342576858">"വിവർത്തനം ചെയ്യുക"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"തിരഞ്ഞെടുത്ത ടെക്സ്റ്റ് വിവർത്തനം ചെയ്യുക"</string>
+ <string name="define" msgid="7394820043869954211">"നിർവചിക്കുക"</string>
+ <string name="define_desc" msgid="7910883642444919726">"തിരഞ്ഞെടുത്ത ടെക്സ്റ്റ് നിർവചിക്കുക"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"സംഭരണയിടം കഴിഞ്ഞു"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"സിസ്റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">ഒരു പൊരുത്തം</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"പൂർത്തിയായി"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB കാർഡ് മായ്ക്കുന്നു…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD കാർഡ് മായ്ക്കുന്നു…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"പങ്കിടുന്ന സ്റ്റോറേജ് മായ്ക്കുന്നു…"</string>
<string name="share" msgid="1778686618230011964">"പങ്കിടുക"</string>
<string name="find" msgid="4808270900322985960">"കണ്ടെത്തുക"</string>
<string name="websearch" msgid="4337157977400211589">"വെബ് തിരയൽ"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS അഭ്യർത്ഥന, USSD അഭ്യർത്ഥനയിലേക്ക് മാറ്റി"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"പുതിയ SS അഭ്യർത്ഥനയിലേക്ക് മാറ്റി"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"മുന്നറിയിപ്പ് നൽകി"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"വികസിപ്പിക്കുക"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ചുരുക്കുക"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"വികസിപ്പിക്കൽ ടോഗിൾ ചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 994b4d9..93f7e37 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi дуудлага | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi дуудлага"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi дуудлага"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Идэвхгүй"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi давуу эрхтэй"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Мобайл давуу эрхтэй"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Таны медиа цуглуулгаас байршлыг унших зөвшөөрлийг аппад олгодог."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> апп баталгаажуулахыг хүсэж байна."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрийн техник хангамж боломжгүй байна"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Таниагүй"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Хурууны хээг дутуу уншуулсан байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Хурууны хээ мэдрэгч бохирдсон байна. Та цэвэрлэсний дараагаар дахин оролдоно уу."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Хуруу хэт удаан хөдөлгөсөн байна. Дахин оролдоно уу."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Таниагүй"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Хурууны хээг нотолсон"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Царайг баталгаажууллаа"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Царайг баталгаажууллаа. Баталгаажуулах товчлуурыг дарна уу"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Апп нь бүртгэлийн синк тохиргоог өөрчлөх боломжтой. Жишээ нь энэ нь Хүмүүс апп бүртгэлтэй синк хийхийг идэвхжүүлэх боломжтой."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"синк статистикийг унших"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Апп нь синк үйлдэлийн түүх болон хэр их дата синк хийгдсэн зэрэг бүртгэлийн синк статусыг унших боломжтой."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"таны USB сангийн агуулгыг унших боломжтой"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"таны SD картны агуулгыг унших боломжтой"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Апп нь таны USB сангийн агуулгыг унших боломжтой."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Апп нь таны SD картны агуулгыг унших боломжтой."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB сангийн агуулгыг өөрчлөх эсвэл устгах"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD картны агуулгыг өөрчлөх болон устгах"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Апп нь USB санруу бичих боломжтой."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Апп нь SD картруу бичих боломжтой."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"таны дундын хадгалах сангийн контентыг унших"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Аппад таны дундын хадгалах сангийн контентыг уншихыг зөвшөөрдөг."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"дундын хадгалах сангийнхаа контентыг өөрчлөх эсвэл устгах"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Аппад таны дундын хадгалах сангийн контентыг бичихийг зөвшөөрдөг."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP дуудлага хийх/хүлээн авах"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Апп-д SIP дуудлага хийх болон хүлээн авахыг зөвшөөрөх."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"шинэ телеком SIM холболтуудыг бүртгэх"</string>
@@ -1087,24 +1083,28 @@
<string name="deleteText" msgid="6979668428458199034">"Устгах"</string>
<string name="inputMethod" msgid="1653630062304567879">"Оруулах арга"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Текст үйлдэл"</string>
- <string name="email" msgid="4560673117055050403">"Имэйл"</string>
+ <string name="email" msgid="4560673117055050403">"Имэйл бичих"</string>
<string name="email_desc" msgid="3638665569546416795">"Сонгосон хаяг руу имэйл илгээх"</string>
<string name="dial" msgid="1253998302767701559">"Залгах"</string>
<string name="dial_desc" msgid="6573723404985517250">"Сонгосон утасны дугаар руу залгах"</string>
- <string name="map" msgid="5441053548030107189">"Газрын зураг"</string>
+ <string name="map" msgid="5441053548030107189">"Газрын зураг хийх"</string>
<string name="map_desc" msgid="1836995341943772348">"Сонгосон хаягийг байршуулах"</string>
<string name="browse" msgid="1245903488306147205">"Нээх"</string>
<string name="browse_desc" msgid="8220976549618935044">"Сонгосон URL-г нээх"</string>
- <string name="sms" msgid="4560537514610063430">"Зурвас"</string>
+ <string name="sms" msgid="4560537514610063430">"Зурвас бичих"</string>
<string name="sms_desc" msgid="7526588350969638809">"Сонгосон утасны дугаар руу мессеж илгээх"</string>
<string name="add_contact" msgid="7867066569670597203">"Нэмэх"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Харилцагчид нэмэх"</string>
<string name="view_calendar" msgid="979609872939597838">"Үзэх"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Календариас сонгосон огноог харах"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Хуваарь"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Хуваарь гаргах"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Aрга хэмжээг сонгосон цагт хуваарилах"</string>
- <string name="view_flight" msgid="7691640491425680214">"Бичлэг"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Хянах"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Сонгосон нислэгийг хянах"</string>
+ <string name="translate" msgid="9218619809342576858">"Орчуулах"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Сонгосон текстийг орчуулах"</string>
+ <string name="define" msgid="7394820043869954211">"Тодорхойлох"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Сонгосон текстийг тодорхойлох"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сангийн хэмжээ дутагдаж байна"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Зарим систем функц ажиллахгүй байна"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 үр дүн гарч ирсэн байна</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Дуусгах"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB санг арилгаж байна…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD картыг цэвэрлэж байна…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Хуваалцсан хадгалах санг устгаж байна…"</string>
<string name="share" msgid="1778686618230011964">"Хуваалцах"</string>
<string name="find" msgid="4808270900322985960">"Олох"</string>
<string name="websearch" msgid="4337157977400211589">"Вэб хайлт"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS хүсэлтийг USSD хүсэлт болгон өөрчилсөн"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Шинэ SS хүсэлт болгон өөрчилсөн"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Ажлын профайл"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Мэдэгдсэн"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Дэлгэх"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Буулгах"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"унтраах/асаах өргөтгөл"</string>
diff --git a/core/res/res/values-mr-watch/strings.xml b/core/res/res/values-mr-watch/strings.xml
index 223f8fa..f8a5214 100644
--- a/core/res/res/values-mr-watch/strings.xml
+++ b/core/res/res/values-mr-watch/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप"</string>
+ <string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"सेन्सर"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index ca7f6df..902380b 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> वाय-फाय"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"वाय-फाय कॉलिंग | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"वाय-फाय कॉलिंग"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"वाय-फाय"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"वायफाय कॉलिंग"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"बंद"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"वाय-फाय अग्रमानांकित"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"प्राधान्य दिलेला मोबाइल"</string>
@@ -187,7 +183,7 @@
<string name="ssl_ca_cert_noti_by_administrator" msgid="3541729986326153557">"आपल्या कार्य प्रोफाइल प्रशासकाद्वारे"</string>
<string name="ssl_ca_cert_noti_managed" msgid="4030263497686867141">"<xliff:g id="MANAGING_DOMAIN">%s</xliff:g> द्वारे"</string>
<string name="work_profile_deleted" msgid="5005572078641980632">"कार्य प्रोफाईल हटविले"</string>
- <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, तुमचे कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
+ <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, तुमचे कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"तुमचे कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्ध नाही"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"बर्याचदा पासवर्ड टाकण्याचा प्रयत्न केला"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"डिव्हाइस व्यवस्थापित केले आहे"</string>
@@ -285,9 +281,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"या डिव्हाइसच्या स्थानावर प्रवेश"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसचे स्थान अॅक्सेस करू द्यायचे?"</string>
- <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अॅप वापरत असताना, अॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अॅप वापरत असताना, अॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
<string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"या डिव्हाइसचे स्थान अॅक्सेस करण्याची <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला नेहेमी अनुमती द्यायची का?"</string>
- <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अॅप वापरत नसलात तरीही, अॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अॅप वापरत नसलात तरीही, अॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"कॅलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"आपल्या कॅलेंडरवर प्रवेश"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला तुमचे कॅलेंडर अॅक्सेस करू द्यायचे?"</string>
@@ -334,11 +330,11 @@
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"फिंगरप्रिंट जेश्चर"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"डिव्हाइसच्या फिंगरप्रिंट सेंसरवर केलेले जेश्चर कॅप्चर करू शकते."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
- <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
- <string name="permdesc_statusBarService" msgid="716113660795976060">"स्टेटस बार होण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_statusBarService" msgid="716113660795976060">"स्टेटस बार होण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"स्टेटस बार विस्तृत करा/संकुचित करा"</string>
- <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_install_shortcut" msgid="4279070216371564234">"शॉर्टकट स्थापित करा"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"अनुप्रयोगाला वापरकर्ता हस्तक्षेपाशिवाय मुख्यस्क्रीन शॉर्टकट जोडण्याची अनुमती देते."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"शॉर्टकट विस्थापित करा"</string>
@@ -348,103 +344,103 @@
<string name="permlab_answerPhoneCalls" msgid="4077162841226223337">"फोन कॉलचे उत्तर द्या"</string>
<string name="permdesc_answerPhoneCalls" msgid="2901889867993572266">"येणार्या फोन कॉलचे उत्तर देण्यास अॅपला अनुमती देते."</string>
<string name="permlab_receiveSms" msgid="8673471768947895082">"मजकूर मेसेज मिळवा (SMS)"</string>
- <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+ <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"मजकूर मेसेज मिळवा (MMS)"</string>
- <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+ <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारण मेसेज वाचा"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्यता घेतलेली फीड वाचा"</string>
- <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"सध्या संकालित केलेल्या फीडविषयी तपशील मिळविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"सध्या संकालित केलेल्या फीडविषयी तपशील मिळविण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_sendSms" msgid="7544599214260982981">"SMS मेसेज पाठवणे आणि पाहणे"</string>
- <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS मेसेज पाठविण्यासाठी अॅप ला अनुमती देते. हे अनपेक्षित शुल्कामुळे होऊ शकते. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय मेसेज पाठवल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
+ <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS मेसेज पाठविण्यासाठी अॅप ला अनुमती देते. हे अनपेक्षित शुल्कामुळे होऊ शकते. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय मेसेज पाठवल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"तुमचे मजकूर मेसेज वाचा (SMS किंवा MMS)"</string>
- <string name="permdesc_readSms" product="tablet" msgid="4741697454888074891">"हा अॅप तुमच्या टॅब्लेटवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
- <string name="permdesc_readSms" product="tv" msgid="5796670395641116592">"हा अॅप तुमच्या टीव्हीवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
- <string name="permdesc_readSms" product="default" msgid="6826832415656437652">"हा अॅप तुमच्या फोनवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="tablet" msgid="4741697454888074891">"हा अॅप तुमच्या टॅब्लेटवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="tv" msgid="5796670395641116592">"हा अॅप तुमच्या टीव्हीवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="default" msgid="6826832415656437652">"हा अॅप तुमच्या फोनवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"मजकूर मेसेज मिळवा (WAP)"</string>
- <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुम्हाला पाठविलेले मेसेज तुम्हाला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
+ <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुम्हाला पाठविलेले मेसेज तुम्हाला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चालणारे अॅप्स पुनर्प्राप्त करा"</string>
- <string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अॅप्लिकेशन वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
+ <string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अॅप्लिकेशन वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफाईल आणि डिव्हाइस मालक व्यवस्थापित करा"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"प्रोफाईल मालक आणि डिव्हाइस मालक सेट करण्याची अॅप्सना अनुमती द्या."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"चालणारे अॅप्स पुनर्क्रमित करा"</string>
- <string name="permdesc_reorderTasks" msgid="7734217754877439351">"समोर आणि पार्श्वभूमीवर कार्ये हलविण्यासाठी अॅप ला अनुमती देते. अॅप हे आपल्या इनपुटशिवाय करू शकतो."</string>
+ <string name="permdesc_reorderTasks" msgid="7734217754877439351">"समोर आणि पार्श्वभूमीवर कार्ये हलविण्यासाठी अॅप ला अनुमती देते. अॅप हे आपल्या इनपुटशिवाय करू शकतो."</string>
<string name="permlab_enableCarMode" msgid="5684504058192921098">"कार मोड सुरू करा"</string>
- <string name="permdesc_enableCarMode" msgid="4853187425751419467">"कार मोड सक्षम करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_enableCarMode" msgid="4853187425751419467">"कार मोड सक्षम करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"अन्य अॅप्स बंद करा"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"अन्य अॅप्सच्या पार्श्वभूमी प्रक्रिया समाप्त करण्यासाठी अॅप ला अनुमती देते. यामुळे अन्य अॅप्स चालणे थांबू शकते."</string>
- <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"हा अॅप इतर अॅप्सच्या शीर्षस्थानी दिसू शकतो."</string>
- <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"हे अॅप इतर अॅप्सच्या शीर्षस्थानी किंवा स्क्रीनच्या इतर भागांवर दिसू शकतो. हे सामान्य अॅप वापरात व्यत्यय आणू शकते किंवा इतर अॅप्सची डिस्प्ले पद्धत बदलू शकते."</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"अन्य अॅप्सच्या पार्श्वभूमी प्रक्रिया समाप्त करण्यासाठी अॅप ला अनुमती देते. यामुळे अन्य अॅप्स चालणे थांबू शकते."</string>
+ <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"हा अॅप इतर अॅप्सच्या शीर्षस्थानी दिसू शकतो."</string>
+ <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"हे अॅप इतर अॅप्सच्या शीर्षस्थानी किंवा स्क्रीनच्या इतर भागांवर दिसू शकतो. हे सामान्य अॅप वापरात व्यत्यय आणू शकते किंवा इतर अॅप्सची डिस्प्ले पद्धत बदलू शकते."</string>
<string name="permlab_runInBackground" msgid="7365290743781858803">"पार्श्वभूमीत चालवा"</string>
- <string name="permdesc_runInBackground" msgid="7370142232209999824">"हे अॅप पार्श्वभूमीत चालू शकते. हे बॅटरी अधिक जलद संपवू शकते."</string>
+ <string name="permdesc_runInBackground" msgid="7370142232209999824">"हे अॅप पार्श्वभूमीत चालू शकते. हे बॅटरी अधिक जलद संपवू शकते."</string>
<string name="permlab_useDataInBackground" msgid="8694951340794341809">"पार्श्वभूमीत डेटा वापरा"</string>
<string name="permdesc_useDataInBackground" msgid="6049514223791806027">"हे अॅप पार्श्वभूमीत डेटा वापरू शकते. हे डेटाचा वापर वाढवू शकते."</string>
- <string name="permlab_persistentActivity" msgid="8841113627955563938">"अॅप नेहमी चालवा"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे टॅबलेट धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
+ <string name="permlab_persistentActivity" msgid="8841113627955563938">"अॅप नेहमी चालवा"</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे टॅबलेट धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"अॅपला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यासाठी अनुमती देते. हे टीव्ही धीमा करून इतर अॅप्सवर उपलब्ध असलेली मेमरी मर्यादित करू शकते."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे फोन धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे फोन धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
<string name="permlab_foregroundService" msgid="3310786367649133115">"पृष्ठभाग सेवा रन करा"</string>
<string name="permdesc_foregroundService" msgid="6471634326171344622">"अॅपला पृष्ठभाग सेवा वापरण्याची अनुमती देते."</string>
- <string name="permlab_getPackageSize" msgid="7472921768357981986">"अॅप संचयन स्थान मोजा"</string>
- <string name="permdesc_getPackageSize" msgid="3921068154420738296">"अॅप ला त्याचा कोड, डेटा आणि कॅश आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
+ <string name="permlab_getPackageSize" msgid="7472921768357981986">"अॅप संचयन स्थान मोजा"</string>
+ <string name="permdesc_getPackageSize" msgid="3921068154420738296">"अॅप ला त्याचा कोड, डेटा आणि कॅश आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"सिस्टम सेटिंग्ज सुधारित करा"</string>
- <string name="permdesc_writeSettings" msgid="7775723441558907181">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
+ <string name="permdesc_writeSettings" msgid="7775723441558907181">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
<string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"सुरूवातीस चालवा"</string>
- <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4525890122209673621">"सिस्टम बूट करणे समाप्त करते तसेच अॅपने स्वतः प्रारंभ करण्यास त्याला अनुमती देते. यामुळे टीव्ही प्रारंभ करण्यासाठी त्यास जास्त वेळ लागू शकतो आणि नेहमी चालू ठेवून संपूर्ण टॅबलेट धीमे करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे फोन प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे फोन प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_broadcastSticky" msgid="7919126372606881614">"रोचक प्रसारण पाठवा"</string>
- <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
+ <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"रोचक प्रसारणे पाठविण्यास अॅपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर तसेच रहाते. अतिरिक्त वापर टीव्ही धीमा किंवा यासाठी बरीच मेमरी वापरली जात असल्यामुळे तो अस्थिर करू शकतो."</string>
- <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
+ <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"तुमचे संपर्क वाचा"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"तुम्ही विशिष्ट लोकांना इतर मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप्सला अनुमती देतात. ही परवागनी अॅप्सला तुमचा संपर्क डेटा सेव्ह करण्यासाठी अनुमती देते आणि दुर्भावनापूर्ण अॅप्स तुम्हाला न कळविता संपर्क डेटा शेअर करू शकतात."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"तुमचे संपर्क सुधारित करा"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅपला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"कॉल लॉग वाचा"</string>
- <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप तुमचा कॉल इतिहास वाचू शकता."</string>
+ <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप तुमचा कॉल इतिहास वाचू शकता."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिहा"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टीव्हीचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अॅक्सेस करा"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_readCalendar" msgid="6716116972752441641">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
- <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
<string name="permlab_writeCalendar" msgid="8438874755193825647">"कॅलेंडर इव्हेंट जोडा किंवा बदला आणि मालकाला न कळवता अतिथींना ईमेल पाठवा"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"हा अॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"हा अॅप आपल्या टीव्हीवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"हा अॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"हा अॅप आपल्या टीव्हीवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्थान प्रदाता आदेश अॅक्सेस करा"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अॅपला अतिरिक्त स्थान प्रदाता आदेशावर प्रवेश करण्याची अनुमती देते. हे कदाचित अॅपला GPS किंवा इतर स्थान स्त्रोत च्या ऑपरेशनमध्ये हस्तक्षेप करण्याची अनुमती देऊ शकते."</string>
<string name="permlab_accessFineLocation" msgid="6265109654698562427">"फक्त फोरग्राउंडमध्ये अचूकपणे अॅक्सेस करा"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच तुमचे अचूक स्थान मिळवू शकते. या स्थान सेवा सुरू करणे आणि त्या वापरण्यासाठी अॅपसाठी तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे, यामुळे बॅटरी वापर वाढू शकतो."</string>
<string name="permlab_accessCoarseLocation" msgid="3707180371693213469">"फक्त फोरग्राउंडमध्ये अंदाजे स्थान (नेटवर्क आधारित) अॅक्सेस करा"</string>
- <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टॅबलेटवर उपलब्ध करणे आवश्यक आहे."</string>
- <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टीव्हीवर उपलब्ध करणे आवश्यक आहे."</string>
- <string name="permdesc_accessCoarseLocation" product="default" msgid="854896049371048754">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. ते वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टॅबलेटवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टीव्हीवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="default" msgid="854896049371048754">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. ते वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे."</string>
<string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"बॅकग्राउंडमध्ये स्थान अॅक्सेस करू शकतो"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"याला अंदाजे किंवा अचूक स्थान अॅक्सेस करण्यास अतिरिक्त मंजूरी दिल्यास, बॅकग्राउंडमध्ये चालतांना अॅप स्थान अॅक्सेस करू शकतो."</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"याला अंदाजे किंवा अचूक स्थान अॅक्सेस करण्यास अतिरिक्त मंजूरी दिल्यास, बॅकग्राउंडमध्ये चालतांना अॅप स्थान अॅक्सेस करू शकतो."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"आपल्या ऑडिओ सेटिंग्ज बदला"</string>
- <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडिओ रेकॉर्ड"</string>
- <string name="permdesc_recordAudio" msgid="4245930455135321433">"हा अॅप कोणत्याही वेळी मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकता."</string>
+ <string name="permdesc_recordAudio" msgid="4245930455135321433">"हा अॅप कोणत्याही वेळी मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकता."</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"सिम वर कमांड पाठवा"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"अॅप ला सिम वर कमांड पाठविण्याची अनुमती देते. हे खूप धोकादायक असते."</string>
<string name="permlab_activityRecognition" msgid="3634590230567608356">"शारीरिक अॅक्टिव्हिटी ओळखा"</string>
- <string name="permdesc_activityRecognition" msgid="3143453925156552894">"हे अॅप तुमच्या शारीरिक अॅक्टिव्हिटी ओळखू शकते."</string>
+ <string name="permdesc_activityRecognition" msgid="3143453925156552894">"हे अॅप तुमच्या शारीरिक अॅक्टिव्हिटी ओळखू शकते."</string>
<string name="permlab_camera" msgid="3616391919559751192">"चित्रे आणि व्हिडिओ घ्या"</string>
- <string name="permdesc_camera" msgid="5392231870049240670">"हा अॅप कोणत्याही वेळी कॅमेरा वापरून चित्रेे घेऊ आणि व्हिडिअो रेकॉर्ड करू शकतो."</string>
+ <string name="permdesc_camera" msgid="5392231870049240670">"हा अॅप कोणत्याही वेळी कॅमेरा वापरून चित्रेे घेऊ आणि व्हिडिअो रेकॉर्ड करू शकतो."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"व्हायब्रेट नियंत्रित करा"</string>
- <string name="permdesc_vibrate" msgid="6284989245902300945">"अॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string>
+ <string name="permdesc_vibrate" msgid="6284989245902300945">"अॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"फोन नंबरवर प्रत्यक्ष कॉल करा"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"आपल्या हस्तक्षेपाशिवाय फोन नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देते. यामुळे अनपेक्षित शुल्क किंवा कॉल लागू शकतात. लक्षात ठेवा की हे आणीबाणीच्या नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देत नाही. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय कॉल केल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"आपल्या हस्तक्षेपाशिवाय फोन नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देते. यामुळे अनपेक्षित शुल्क किंवा कॉल लागू शकतात. लक्षात ठेवा की हे आणीबाणीच्या नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देत नाही. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय कॉल केल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS कॉल सेवा अॅक्सेस करा"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"आपल्या हस्तक्षेपाशिवाय अॅपला कॉल करण्यासाठी IMS सेवा वापरण्याची अनुमती देते."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"फोन स्थिती आणि ओळख वाचा"</string>
@@ -458,59 +454,59 @@
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"टॅबलेट निष्क्रिय होण्यापासून प्रतिबंधित करा"</string>
<string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"निष्क्रिय होण्यापासून प्रतिबंध करा"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"फोन निष्क्रिय होण्यापासून प्रतिबंधित करा"</string>
- <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"टॅब्लेटला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"टॅब्लेटला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"निष्क्रिय होण्यापासून टीव्हीला प्रतिबंध करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"फोनला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"फोनला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_transmitIr" msgid="7545858504238530105">"इन्फ्रारेड प्रक्षेपण करा"</string>
<string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"अॅप ला टॅब्लेटच्या इन्फ्रारेड ट्रान्समीटरचा वापर करण्याची अनुमती देते."</string>
<string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"टीव्हीचे इन्फ्रारेड ट्रान्समीटर वापरण्यासाठी अॅपला अनुमती देते."</string>
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"अॅप ला फोनच्या इन्फ्रारेड ट्रान्समीटरचा वापर करण्याची अनुमती देते."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"वॉलपेपर सेट करा"</string>
- <string name="permdesc_setWallpaper" msgid="7373447920977624745">"सिस्टम वॉलपेपर सेट करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setWallpaper" msgid="7373447920977624745">"सिस्टम वॉलपेपर सेट करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"तुमचा वॉलपेपर आकार समायोजित करा"</string>
- <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"सिस्टम वॉलपेपर आकार सूचना सेट करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"सिस्टम वॉलपेपर आकार सूचना सेट करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"टाइम झोन सेट करा"</string>
- <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"टॅब्लेटचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"टॅब्लेटचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"टीव्हीचा टाईम झोन बदलण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"फोनचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"फोनचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"डिव्हाइसवरील खाती शोधा"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"टॅब्लेटद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट होऊ शकतात."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"टॅब्लेटद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट होऊ शकतात."</string>
<string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"टीव्हीद्वारे ज्ञात खात्यांची सूची मिळविण्यासाठी अॅपला अनुमती देतो. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट असू शकतात."</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट करू शकतात."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट करू शकतात."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"नेटवर्क कनेक्शन पहा"</string>
- <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"कोणती नेटवर्क अस्तित्वात आहेत आणि कनेक्ट केलेली आहेत यासारख्या नेटवर्क कनेक्शनविषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"कोणती नेटवर्क अस्तित्वात आहेत आणि कनेक्ट केलेली आहेत यासारख्या नेटवर्क कनेक्शनविषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_createNetworkSockets" msgid="7934516631384168107">"पूर्ण नेटवर्क प्रवेश आहे"</string>
- <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सॉकेट तयार करण्यासाठी आणि कस्टम नेटवर्क प्रोटोकॉल वापरण्यासाठी अॅप ला अनुमती देते. ब्राउझर आणि अन्य अॅप्लिकेशन म्हणजे इंटरनेटवर डेटा पाठवण्याचा मार्ग, म्हणजे इंटरनेटवर डेटा पाठविण्यासाठी परवानगीची आवश्यकता नसते."</string>
+ <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सॉकेट तयार करण्यासाठी आणि कस्टम नेटवर्क प्रोटोकॉल वापरण्यासाठी अॅप ला अनुमती देते. ब्राउझर आणि अन्य अॅप्लिकेशन म्हणजे इंटरनेटवर डेटा पाठवण्याचा मार्ग, म्हणजे इंटरनेटवर डेटा पाठविण्यासाठी परवानगीची आवश्यकता नसते."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"नेटवर्क कनेक्टिव्हिटी बदला"</string>
- <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"टिथर केलेली कनेक्टिव्हिटी बदला"</string>
- <string name="permdesc_changeTetherState" msgid="1524441344412319780">"टेदर केलेल्या नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeTetherState" msgid="1524441344412319780">"टेदर केलेल्या नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"वाय-फाय कनेक्शन पहा"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"वाय-फाय सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या वाय-फाय डीव्हाइसचे नाव यासारख्या, वाय-फाय नेटवर्किंग विषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"वाय-फाय सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या वाय-फाय डीव्हाइसचे नाव यासारख्या, वाय-फाय नेटवर्किंग विषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"वाय-फाय वरून कनेक्ट करा आणि डिस्कनेक्ट करा"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"वाय-फाय अॅक्सेस बिंदूंवर कनेक्ट करण्यासाठी आणि त्यावरून डिस्कनेक्ट करण्यासाठी आणि वाय-फाय नेटवर्कसाठी डिव्हाइस कॉंफिगरेशनमध्ये बदल करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"वाय-फाय मल्टिकास्ट रिसेप्शनला अनुमती द्या"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या टॅब्लेटवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या टॅब्लेटवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"केवळ तुमचा टीव्ही न वापरता, एकाधिक पत्ते वापरून एका वाय-फाय नेटवकवरील सर्व डीव्हाइसवर पाठविलेली पॅकेट प्राप्त करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"ब्लूटूथ सेटिंग्ज अॅक्सेस करा"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"स्थानिक ब्लूटूथ टीव्ही कॉंफिगर करण्यासाठी आणि दूरस्थ डीव्हाइससह शोधण्यासाठी आणि जोडण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX कनेक्ट करा आणि त्यावरून डिस्कनेक्ट करा"</string>
- <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX स्थिती बदला"</string>
- <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX नेटवर्कवर टॅबलेट कनेक्ट करण्यास आणि त्यावरून टॅबलेट डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX नेटवर्कवर टॅबलेट कनेक्ट करण्यास आणि त्यावरून टॅबलेट डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"WiMAX नेटवर्कवरून टीव्ही कनेक्ट करण्यासाठी आणि त्यावरून टीव्ही डिस्कनेक्ट करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ब्लूटूथ डीव्हाइससह जोडा"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
<string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"टीव्हीवर ब्लूटूथचे कॉंफिगरेशन पाहण्यासाठी आणि जोडलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"फील्ड जवळील कम्युनिकेशन नियंत्रित करा"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"फील्ड जवळील कम्युनिकेशन (NFC) टॅग, कार्डे आणि वाचक यांच्यासह संवाद करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"तुमचे स्क्रीन लॉक अक्षम करा"</string>
- <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
+ <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेअर वापरा"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ऑथेंटिकेशनसाठी बायोमेट्रिक हार्डवेअरचा वापर करण्याची अॅपला अनुमती देते"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"फिंगरप्रिंट हार्डवेअर व्यवस्थापित करा"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"अॅपला तुमच्या मीडिया संग्रहामध्येील स्थाने वाचण्यासाठी अनुमती देते."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"अॅप्लिकेशन <xliff:g id="APP">%s</xliff:g>ला ऑथेंटिकेट करायचे आहे."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेअर उपलब्ध नाही"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ओळखले नाही"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फिंगरप्रिंट आढळली. कृपया पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फिंगरप्रिंटवर प्रक्रिया करणे शक्य झाले नाही. कृपया पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फिंगरप्रिंट सेन्सर खराब आहे. कृपया साफ करा आणि पुन्हा प्रयत्न करा."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"बोट खूप सावकाश हलविले. कृपया पुन्हा प्रयत्न करा."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"ओळखले नाही"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"चेहरा ऑथेंटिकेशन केलेला आहे"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कंफर्म दाबा"</string>
@@ -591,19 +591,15 @@
</string-array>
<string name="face_icon_content_description" msgid="4024817159806482191">"चेहरा आयकन"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"सिंक सेटिंग्ज वाचा"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"सिंक चालू आणि बंद करा टॉगल करा"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"खात्यासाठी सिंक सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांच्या अॅप चे सिंक सक्षम करण्यासाठी वापरले जाऊ शकते."</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"खात्यासाठी सिंक सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांच्या अॅप चे सिंक सक्षम करण्यासाठी वापरले जाऊ शकते."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"सिंक आकडेवारी वाचा"</string>
- <string name="permdesc_readSyncStats" msgid="1510143761757606156">"सिंक इव्हेंटचा इतिहास आणि किती डेटाचे सिंक केले आहे यासह, खात्याची सिंक स्थिती वाचण्यासाठी अॅप ला अनुमती देते."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"आपल्या USB संचयनाची सामग्री वाचा"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"आपल्या SD कार्डची सामग्री वाचा"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"अॅपला आपल्या USB संचयनाची सामग्री वाचण्याची अनुमती देते."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"अॅपला आपल्या SD कार्डची सामग्री वाचण्याची अनुमती देते."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"आपल्या USB संचयनाची सामग्री सुधारित करा किंवा हटवा"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"आपल्या SD कार्डची सामग्री सुधारित करा किंवा हटवा"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB संचयनावर लिहिण्यासाठी अॅप ला अनुमती देते."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SD कार्डवर लिहिण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_readSyncStats" msgid="1510143761757606156">"सिंक इव्हेंटचा इतिहास आणि किती डेटाचे सिंक केले आहे यासह, खात्याची सिंक स्थिती वाचण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"तुमच्या शेअर केलेल्या स्टोरेजचे आशय वाचते"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"अॅपला तुमच्या शेअर केलेल्या स्टोरेजचे आशय वाचण्याची अनुमती देते."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"तुमच्या शेअर केलेल्या स्टोरेजच्या आशयांमध्ये सुधारणा करा किंवा हटवा"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"अॅपला तुमच्या शेअर केलेल्या स्टोरेजचे आशय लिहिण्याची अनमती देते."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP कॉल करा/प्राप्त करा"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"अॅपला SIP कॉल करण्याची आणि प्राप्त करण्याची अनुमती देते."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"नवीन टेलिकॉम सिम कनेक्शनची नोंदणी करा"</string>
@@ -611,17 +607,17 @@
<string name="permlab_register_call_provider" msgid="108102120289029841">"नवीन टेलिकॉम कनेक्शनची नोंदणी करा"</string>
<string name="permdesc_register_call_provider" msgid="7034310263521081388">"नवीन टेलिकॉम कनेक्शनची नोंदणी करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_connection_manager" msgid="1116193254522105375">"टेलिकॉम कनेक्शन व्यवस्थापित करा"</string>
- <string name="permdesc_connection_manager" msgid="5925480810356483565">"टेलिकॉम कनेक्शन व्यवस्थापित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_connection_manager" msgid="5925480810356483565">"टेलिकॉम कनेक्शन व्यवस्थापित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_bind_incall_service" msgid="6773648341975287125">"कॉल-मधील स्क्रीनशी परस्परसंवाद करा"</string>
<string name="permdesc_bind_incall_service" msgid="8343471381323215005">"वापरकर्ता कॉल-मधील स्क्रीन केव्हा आणि कशी पाहतो ते नियंत्रित करण्याची अॅपला अनुमती देते."</string>
<string name="permlab_bind_connection_service" msgid="3557341439297014940">"टेलिफोनी सेवांशी परस्परसंवाद साधा"</string>
- <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"कॉल करण्यासाठी/घेण्यासाठी टेलिफोनी सेवांशी परस्परसंवाद साधण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"कॉल करण्यासाठी/घेण्यासाठी टेलिफोनी सेवांशी परस्परसंवाद साधण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_control_incall_experience" msgid="9061024437607777619">"एक कॉल-मधील वापरकर्ता अनुभव प्रदान करा"</string>
<string name="permdesc_control_incall_experience" msgid="915159066039828124">"अॅप्सला कॉल-मधील वापरकर्ता अनुभव प्रदान करण्याची अनुमती देते."</string>
<string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"ऐतिहासिक नेटवर्क वापर वाचा"</string>
<string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"विशिष्ट नेटवर्क आणि अॅप्सकरिता ऐतिहासिक नेटवर्क वापराचे वाचन करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"नेटवर्क धोरण व्यवस्थापित करा"</string>
- <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"नेटवर्क धोरणे व्यवस्थापित करण्यासाठी आणि अॅप-विशिष्ट नियम परिभाषित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"नेटवर्क धोरणे व्यवस्थापित करण्यासाठी आणि अॅप-विशिष्ट नियम परिभाषित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"नेटवर्क वापर हिशोब सुधारित करा"</string>
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"अॅप्स वर नेटवर्क वापराचा हिशोब कसा घेतला जातो हे सुधारित करण्यासाठी अॅप्स ला अनुमती देते. सामान्य अॅप्सद्वारे वापरण्यासाठी नाही."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"प्रवेश सूचना"</string>
@@ -637,7 +633,7 @@
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"नेटवर्क स्थितींवरील निरीक्षणांसाठी ऐका"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"अनु्प्रयोगाला नेटवर्क स्थितींवरील निरीक्षणे ऐकण्यासाठी अनुमती देते. सामान्य अॅप्ससाठी कधीही आवश्यक नसावे."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string>
- <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"स्पर्श स्क्रीनची कॅलिब्रेशन प्राचले सुधारित करण्यासाठी अॅप ला अनुमती देते. सामान्य अॅप्स साठी कधीही आवश्यक नसते."</string>
+ <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"स्पर्श स्क्रीनची कॅलिब्रेशन प्राचले सुधारित करण्यासाठी अॅप ला अनुमती देते. सामान्य अॅप्स साठी कधीही आवश्यक नसते."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM प्रमाणपत्रे अॅक्सेस करा"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM प्रमाणपत्रांची तरतूद करण्यासाठी आणि वापरण्यासाठी अनुप्रयोगास अनुमती देते. सामान्य अॅप्सकरिता कधीही आवश्यकता नसते."</string>
<string name="permlab_handoverStatus" msgid="7820353257219300883">"Android बीम स्थानांतरण स्थिती प्राप्त करा"</string>
@@ -676,7 +672,7 @@
<string name="policylab_expirePassword" msgid="5610055012328825874">"स्क्रीन लॉक पासवर्ड कालबाह्यता सेट करा"</string>
<string name="policydesc_expirePassword" msgid="5367525762204416046">"लॉक-स्क्रीन पासवर्ड किती वारंवार बदलणे आवश्यक आहे ते बदला."</string>
<string name="policylab_encryptedStorage" msgid="8901326199909132915">"स्टोरेज एंक्रिप्शन सेट करा"</string>
- <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"स्टोअर केलेला अॅप डेटा एंक्रिप्ट केला जाणे आवश्यक आहे."</string>
+ <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"स्टोअर केलेला अॅप डेटा एंक्रिप्ट केला जाणे आवश्यक आहे."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"कॅमेरे अक्षम करा"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"सर्व डिव्हाइस कॅमेर्यांचा वापर प्रतिबंधित करा."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"काही स्क्रीन लॉक वैशिष्ट्ये अक्षम करा"</string>
@@ -901,7 +897,7 @@
<string name="granularity_label_link" msgid="5815508880782488267">"लिंक"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"रेखा"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"फॅक्टरी चाचणी अयशस्वी"</string>
- <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया फक्त /सिस्टम/अॅप मध्ये इंस्टॉल केलेल्या पॅकेजसाठी समर्थित आहे."</string>
+ <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया फक्त /सिस्टम/अॅप मध्ये इंस्टॉल केलेल्या पॅकेजसाठी समर्थित आहे."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST क्रिया प्रदान करणारे कोणतेही पॅकेज आढळले नाही."</string>
<string name="factorytest_reboot" msgid="6320168203050791643">"रीबूट करा"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"\"<xliff:g id="TITLE">%s</xliff:g>\" वरील पृष्ठ हे म्हणते:"</string>
@@ -932,17 +928,17 @@
<string name="autofill_area" msgid="3547409050889952423">"क्षेत्र"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"अमिरात"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"तुमचे वेब बुकमार्क आणि इतिहास वाचा"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"ब्राउझरने भेट दिलेल्या सर्व URL चा इतिहास आणि ब्राउझरचे सर्व बुकमार्क वाचण्यास अॅप ला अनुमती देते. टीप: या परवानगीची तृतीय-पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमता असलेल्या अन्य अनुप्रयोगांद्वारे अंमलबजावणी करू शकत नाही."</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"ब्राउझरने भेट दिलेल्या सर्व URL चा इतिहास आणि ब्राउझरचे सर्व बुकमार्क वाचण्यास अॅप ला अनुमती देते. टीप: या परवानगीची तृतीय-पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमता असलेल्या अन्य अनुप्रयोगांद्वारे अंमलबजावणी करू शकत नाही."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"वेब बुकमार्क आणि इतिहास लिहा"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"तुमच्या टॅब्लेटवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"तुमच्या टॅब्लेटवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"तुमच्या टीव्हीवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅपला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅपला अनुमती देऊ शकते. टीप: या परवानगीची अंमलबजावणी वेब ब्राउझिंग क्षमता असलेल्या तृतीय-पक्ष ब्राउझरद्वारे किंवा इतर अॅप्लिकेशनद्वारे केली जाऊ शकत नाही."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"तुमच्या फोनवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"तुमच्या फोनवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"अलार्म सेट करा"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"इंस्टॉल केलेल्या अलार्म घड्याळ अॅपमध्ये अलार्म सेट करण्यासाठी अॅपला अनुमती देते. काही अलार्म घड्याळ अॅप्समध्ये हे वैशिष्ट्य नसू शकते."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"व्हॉइसमेल जोडा"</string>
- <string name="permdesc_addVoicemail" msgid="6604508651428252437">"आपल्या व्हॉइसमेल इनबॉक्समध्ये मेसेज जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_addVoicemail" msgid="6604508651428252437">"आपल्या व्हॉइसमेल इनबॉक्समध्ये मेसेज जोडण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउझर भौगोलिक स्थान परवानग्या सुधारित करा"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउझरच्या भौगोलिक स्थान परवानग्या सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स यादृच्छिक वेबसाइटवर स्थान माहिती पाठविण्यास अनुमती देण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउझरच्या भौगोलिक स्थान परवानग्या सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स यादृच्छिक वेबसाइटवर स्थान माहिती पाठविण्यास अनुमती देण्यासाठी याचा वापर करू शकतात."</string>
<string name="save_password_message" msgid="767344687139195790">"ब्राउझरने हा पासवर्ड लक्षात ठेवावा असे तुम्ही इच्छिता?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"आत्ता नाही"</string>
<string name="save_password_remember" msgid="6491879678996749466">"लक्षात ठेवा"</string>
@@ -1087,24 +1083,28 @@
<string name="deleteText" msgid="6979668428458199034">"हटवा"</string>
<string name="inputMethod" msgid="1653630062304567879">"इनपुट पद्धत"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"मजकूर क्रिया"</string>
- <string name="email" msgid="4560673117055050403">"ईमेल"</string>
+ <string name="email" msgid="4560673117055050403">"ईमेल करा"</string>
<string name="email_desc" msgid="3638665569546416795">"निवडलेल्या अॅड्रेसवर ईमेल करा"</string>
<string name="dial" msgid="1253998302767701559">"कॉल करा"</string>
<string name="dial_desc" msgid="6573723404985517250">"निवडलेल्या फोन नंबरवर कॉल करा"</string>
- <string name="map" msgid="5441053548030107189">"नकाशा"</string>
+ <string name="map" msgid="5441053548030107189">"नकाशा उघडा"</string>
<string name="map_desc" msgid="1836995341943772348">"निवडलेला पत्ता शोधा"</string>
<string name="browse" msgid="1245903488306147205">"उघडा"</string>
<string name="browse_desc" msgid="8220976549618935044">"निवडलेली URL उघडा"</string>
- <string name="sms" msgid="4560537514610063430">"मेसेज"</string>
+ <string name="sms" msgid="4560537514610063430">"मेसेज करा"</string>
<string name="sms_desc" msgid="7526588350969638809">"निवडलेल्या फोन नंबरवर एसएमएस करा"</string>
<string name="add_contact" msgid="7867066569670597203">"जोडा"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"संपर्कांमध्ये जोडा"</string>
- <string name="view_calendar" msgid="979609872939597838">"पहा"</string>
+ <string name="view_calendar" msgid="979609872939597838">"पाहा"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"निवडलेली वेळ कॅलेंडरमध्ये पाहा"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"शेड्यूल"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"निवडलेल्या वेळेसाठी इव्हेंट शेड्यूल करा"</string>
- <string name="view_flight" msgid="7691640491425680214">"ट्रॅक"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"शेड्युल करा"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"निवडलेल्या वेळेसाठी इव्हेंट शेड्युल करा"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ट्रॅक करा"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"निवडलेले विमान ट्रॅक करा"</string>
+ <string name="translate" msgid="9218619809342576858">"भाषांतर करा"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"निवडलेल्या मजकुराचे भाषांतर करा"</string>
+ <string name="define" msgid="7394820043869954211">"व्याख्या सांगा"</string>
+ <string name="define_desc" msgid="7910883642444919726">"निवडलेल्या मजकुराची व्याख्या सांगा"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"संचयन स्थान संपत आहे"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
@@ -1143,18 +1143,18 @@
<string name="use_a_different_app" msgid="8134926230585710243">"एक भिन्न अॅप वापरा"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"डाउनलोड केलेल्या सिस्टम सेटिंग्ज > Apps > मधील डीफॉल्ट साफ करा."</string>
<string name="chooseActivity" msgid="7486876147751803333">"क्रिया निवडा"</string>
- <string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिव्हाइससाठी अॅप निवडा"</string>
+ <string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिव्हाइससाठी अॅप निवडा"</string>
<string name="noApplications" msgid="2991814273936504689">"कोणतेही अॅप्स ही क्रिया करू शकत नाहीत."</string>
<string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> थांबला आहे"</string>
<string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> थांबली आहे"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> थांबतो"</string>
<string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> थांबते"</string>
- <string name="aerr_restart" msgid="7581308074153624475">"अॅप पुन्हा उघडा"</string>
+ <string name="aerr_restart" msgid="7581308074153624475">"अॅप पुन्हा उघडा"</string>
<string name="aerr_report" msgid="5371800241488400617">"अभिप्राय पाठवा"</string>
<string name="aerr_close" msgid="2991640326563991340">"बंद करा"</string>
<string name="aerr_mute" msgid="1974781923723235953">"डिव्हाइस रीस्टार्ट होईपर्यंत म्यूट करा"</string>
<string name="aerr_wait" msgid="3199956902437040261">"प्रतीक्षा करा"</string>
- <string name="aerr_close_app" msgid="3269334853724920302">"अॅप बंद करा"</string>
+ <string name="aerr_close_app" msgid="3269334853724920302">"अॅप बंद करा"</string>
<string name="anr_title" msgid="4351948481459135709"></string>
<string name="anr_activity_application" msgid="8493290105678066167">"<xliff:g id="APPLICATION">%2$s</xliff:g> प्रतिसाद देत नाही"</string>
<string name="anr_activity_process" msgid="1622382268908620314">"<xliff:g id="ACTIVITY">%1$s</xliff:g> प्रतिसाद देत नाही"</string>
@@ -1164,7 +1164,7 @@
<string name="report" msgid="4060218260984795706">"अहवाल द्या"</string>
<string name="wait" msgid="7147118217226317732">"प्रतीक्षा करा"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"पृष्ठ प्रतिसाद न देणारे झाले आहे.\n\nतुम्ही हे बंद करू इच्छिता?"</string>
- <string name="launch_warning_title" msgid="1547997780506713581">"अॅप पुनर्निर्देशित केला"</string>
+ <string name="launch_warning_title" msgid="1547997780506713581">"अॅप पुनर्निर्देशित केला"</string>
<string name="launch_warning_replace" msgid="6202498949970281412">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता चालत आहे."</string>
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> मूळतः लाँच केले."</string>
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"स्केल"</string>
@@ -1175,7 +1175,7 @@
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे Android OS च्या विसंगत आवृत्तीसाठी तयार केले होते आणि ते अनपेक्षित पद्धतीने काम करू शकते. अॅपची अपडेट केलेली आवृत्ती उपलब्ध असू शकते."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"नेहमी दर्शवा"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"अपडेट आहे का ते तपासा"</string>
- <string name="smv_application" msgid="3307209192155442829">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
+ <string name="smv_application" msgid="3307209192155442829">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
<string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> प्रक्रियेने तिच्या स्वतः-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले."</string>
<string name="android_upgrading_title" product="default" msgid="7513829952443484438">"फोन अपडेट होत आहे…"</string>
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"टॅबलेट अपडेट होत आहे…"</string>
@@ -1186,7 +1186,7 @@
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"संचयन ऑप्टिमाइझ करत आहे."</string>
<string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"सिस्टम अपडेट संपत आहे…"</string>
<string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> श्रेणीसुधारित करत आहे…"</string>
- <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
+ <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"अॅप्स प्रारंभ करत आहे."</string>
<string name="android_upgrading_complete" msgid="1405954754112999229">"बूट समाप्त होत आहे."</string>
@@ -1288,7 +1288,7 @@
<string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर फोन कनेक्ट केलेला असताना तो वाय-फाय वरून तात्पुरता डिस्कनेक्ट केला जाईल"</string>
<string name="select_character" msgid="3365550120617701745">"वर्ण घाला"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS मेसेज पाठवत आहे"</string>
- <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> मोठ्या संख्येने SMS मेसेज पाठवत आहे. तुम्ही या अॅप ला मेसेज पाठविणे सुरु ठेवण्याची अनुमती देऊ इच्छिता?"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> मोठ्या संख्येने SMS मेसेज पाठवत आहे. तुम्ही या अॅप ला मेसेज पाठविणे सुरु ठेवण्याची अनुमती देऊ इच्छिता?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"अनुमती द्या"</string>
<string name="sms_control_no" msgid="625438561395534982">"नकार द्या"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> हा <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>वर एक मेसेज पाठवू इच्छितो."</string>
@@ -1445,7 +1445,7 @@
<string name="no_file_chosen" msgid="6363648562170759465">"फाईल निवडली नाही"</string>
<string name="reset" msgid="2448168080964209908">"रीसेट करा"</string>
<string name="submit" msgid="1602335572089911941">"सबमिट करा"</string>
- <string name="car_mode_disable_notification_title" msgid="5704265646471239078">"ड्रायव्हिंग अॅप चालू आहे"</string>
+ <string name="car_mode_disable_notification_title" msgid="5704265646471239078">"ड्रायव्हिंग अॅप चालू आहे"</string>
<string name="car_mode_disable_notification_message" msgid="7647248420931129377">"ड्रायव्हिंग अॅपमधून बाहेर पाडण्यासाठी टॅप करा."</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
<string name="tethered_notification_message" msgid="2113628520792055377">"सेट करण्यासाठी टॅप करा."</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g> पैकी <xliff:g id="INDEX">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"पूर्ण केले"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB स्टोरेज मिटवत आहे…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD कार्ड मिटवत आहे…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"शेअर केलेले स्टोरेज मिटवत आहे…"</string>
<string name="share" msgid="1778686618230011964">"शेअर करा"</string>
<string name="find" msgid="4808270900322985960">"शोधा"</string>
<string name="websearch" msgid="4337157977400211589">"वेब शोध"</string>
@@ -1775,7 +1774,7 @@
<string name="package_deleted_device_owner" msgid="2307122077550236438">"आपल्या प्रशासकाने हटवले"</string>
<string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"बॅटरी लाइफ वाढवण्यासाठी, बॅटरी सेव्हर काही डिव्हाइस वैशिष्ट्ये बंद करते आणि अॅप्सना प्रतिबंधित करते. "<annotation id="url">"अधिक जाणून घ्या"</annotation></string>
<string name="battery_saver_description" msgid="769989536172631582">"बॅटरी लाइफ वाढवण्यासाठी, बॅटरी सेव्हर काही वैशिष्ट्ये बंद करते आणि अॅप्स प्रतिबंधित करते."</string>
- <string name="data_saver_description" msgid="6015391409098303235">"डेटा वापर कमी करण्यात मदत करण्यासाठी, डेटा सर्व्हर काही अॅप्सना पार्श्वभूमीमध्ये डेटा पाठविण्यास किंवा प्राप्त करण्यास प्रतिबंधित करतो. तुम्ही सध्या वापरत असलेला अॅप डेटामध्ये प्रवेश करू शकतो परंतु तसे तो खूप कमी वेळा करू शकतो. याचा अर्थ, उदाहरणार्थ, तुम्ही इमेज टॅप करेपर्यंत त्या प्रदर्शित करणार नाहीत असा असू शकतो."</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"डेटा वापर कमी करण्यात मदत करण्यासाठी, डेटा सर्व्हर काही अॅप्सना पार्श्वभूमीमध्ये डेटा पाठविण्यास किंवा प्राप्त करण्यास प्रतिबंधित करतो. तुम्ही सध्या वापरत असलेला अॅप डेटामध्ये प्रवेश करू शकतो परंतु तसे तो खूप कमी वेळा करू शकतो. याचा अर्थ, उदाहरणार्थ, तुम्ही इमेज टॅप करेपर्यंत त्या प्रदर्शित करणार नाहीत असा असू शकतो."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"डेटा बचतकर्ता चालू करायचा?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"चालू करा"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS विनंती USSD विनंतीवर बदलली"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"नवीन SS विनंतीवर बदलली"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"कार्य प्रोफाईल"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"सूचना दिल्या"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"विस्तृत करा"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"संकुचित करा"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"टॉगल विस्तार"</string>
@@ -1863,16 +1861,16 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"सर्व भाषा"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"सर्व प्रदेश"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"शोध"</string>
- <string name="app_suspended_title" msgid="2075071241147969611">"अॅप उपलब्ध नाही"</string>
+ <string name="app_suspended_title" msgid="2075071241147969611">"अॅप उपलब्ध नाही"</string>
<string name="app_suspended_default_message" msgid="123166680425711887">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> आत्ता उपलब्ध नाही. हे <xliff:g id="APP_NAME_1">%2$s</xliff:g> कडून व्यवस्थापित केले जाते."</string>
<string name="app_suspended_more_details" msgid="1131804827776778187">"अधिक जाणून घ्या"</string>
<string name="work_mode_off_title" msgid="1118691887588435530">"कार्य प्रोफाइल चालू ठेवायची?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"तुमची कार्य अॅप्स, सूचना, डेटा आणि अन्य कार्य प्रोफाइल वैशिष्ट्ये चालू केली जातील"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"चालू करा"</string>
- <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
<string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"अपडेट आहे का ते तपासा"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"आपल्याकडे नवीन मेसेज आहेत"</string>
- <string name="new_sms_notification_content" msgid="7002938807812083463">"पाहण्यासाठी SMS अॅप उघडा"</string>
+ <string name="new_sms_notification_content" msgid="7002938807812083463">"पाहण्यासाठी SMS अॅप उघडा"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"काही कार्यक्षमता मर्यादित असू शकतात"</string>
<string name="user_encrypted_message" msgid="4923292604515744267">"अनलॉक करण्यासाठी टॅप करा"</string>
<string name="user_encrypted_detail" msgid="5708447464349420392">"वापरकर्ता डेटा लॉक केला"</string>
@@ -1882,7 +1880,7 @@
<string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"फायली पाहण्यासाठी टॅप करा"</string>
<string name="pin_target" msgid="3052256031352291362">"पिन"</string>
<string name="unpin_target" msgid="3556545602439143442">"अनपिन करा"</string>
- <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
+ <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ करत आहे..."</string>
<string name="demo_restarting_message" msgid="952118052531642451">"डिव्हाइस रीसेट करत आहे..."</string>
@@ -1947,13 +1945,13 @@
<string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="4860853725206702336">"अॅपची आवृत्ती डाउनग्रेड केली, किंवा ती या शॉर्टकटशी कंपॅटिबल नाही"</string>
- <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
- <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
+ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद केलेला आहे"</string>
<string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"अनइंस्टॉल करा"</string>
<string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"तरीही उघडा"</string>
- <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अॅप आढळला"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अॅप आढळला"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवायचे आहेत"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"संपादित करा"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"कॉल आणि सूचनांवर व्हायब्रेट होईल"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index a1af8f2..6492f48 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Panggilan Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Panggilan Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Panggilan Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Mati"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi diutamakan"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mudah alih diutamakan"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Membenarkan apl membaca lokasi daripada koleksi media anda."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikasi <xliff:g id="APP">%s</xliff:g> mahu membuat pengesahan."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Perkakasan biometrik tidak tersedia"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Cap jari separa dikesan. Sila cuba lagi."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Penderia cap jari kotor. Sila bersihkan dan cuba lagi."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Jari digerakkan terlalu perlahan. Sila cuba lagi."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Cap jari disahkan"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Wajah disahkan"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Wajah disahkan, sila tekan sahkan"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Membenarkan apl mengubah suai tetapan segerak untuk akaun. Sebagai contoh, ini boleh digunakan untuk mendayakan penyegerakan apl Orang dengan akaun."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"baca statistik penyegerakan"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Membenarkan apl untuk membaca statistik segerak untuk akaun, termasuk sejarah acara segerak dan berapa banyak data disegerakkan."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"baca kandungan storan USB anda"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"baca kandungan kad SD anda"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Membenarkan aplikasi membaca kandungan storan USB anda."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Membenarkan apl membaca kandungan kad SD anda."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ubah suai atau padam kandungan storan USB anda"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ubah suai atau padam kandungan kad SD anda"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Membenarkan apl menulis ke storan USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Membenarkan apl menulis ke kad SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"baca kandungan storan kongsi anda"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Membenarkan apl membaca kandungan storan kongsi anda."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"mengubah suai atau memadamkan kandungan storan kongsi anda"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Membenarkan apl menulis kandungan storan kongsi anda."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"buat/terima panggilan SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Membenarkan apl membuat dan menerima panggilan SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"daftar sambungan SIM telekom baharu"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Jadualkan acara untuk masa yang dipilih"</string>
<string name="view_flight" msgid="7691640491425680214">"Pantau"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Jejak penerbangan yang dipilih"</string>
+ <string name="translate" msgid="9218619809342576858">"Terjemah"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Terjemahkan teks yang dipilih"</string>
+ <string name="define" msgid="7394820043869954211">"Takrifkan"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Takrifkan teks yang dipilih"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang storan semakin berkurangan"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 padanan</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Selesai"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Memadamkan storan USB…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Memadamkan kad SD…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Memadamkan storan kongsi…"</string>
<string name="share" msgid="1778686618230011964">"Kongsi"</string>
<string name="find" msgid="4808270900322985960">"Dapatkan"</string>
<string name="websearch" msgid="4337157977400211589">"Carian Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Permintaan SS ditukar kepada permintaan USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Bertukar kepada permintaan SS baharu"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil kerja"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Dimaklumkan"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Kembangkan"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Runtuhkan"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"togol pengembangan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 372be10..a3da75e 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi ခေါ်ဆိုမှု | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi ခေါ်ဆိုမှု"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WiFi ခေါ်ဆိုမှု"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ပိတ်ထားရသည်"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"ဝိုင်ဖိုင်အား ပိုနှစ်သက်သော"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"မိုဘိုင်းကို အသုံးပြုလိုပါသည်"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"အက်ပ်အား သင့်မီဒီယာစုစည်းမှုမှ တည်နေရာများကို ဖတ်ခွင့်ပေးသည်။"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"အပလီကေးရှင်း <xliff:g id="APP">%s</xliff:g> က အထောက်အထားစိစစ်လိုသည်။"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ဇီဝအချက်အလက်သုံး ကွန်ပျူတာစက်ပစ္စည်း မရရှိနိုင်ပါ"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"မသိပါ"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"လက်ဗွေရဦ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"လက်ဗွေရာယူခြင်း မဆောင်ရွက်နိုင်ပါ။ ထပ်မံကြိုးစားပါ။"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"လက်ဗွေရာဖတ်ကိရိယာ ညစ်ပေနေသည်။ ကျေးဇူးပြု၍ ရှင်းလင်းကာ ထပ်မံကြိုးစားပါ။"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"လက်ညှိုးအလွန်နှေးကွေးစွာ ရွေ့ခဲ့သည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"မသိပါ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"အကောင့်တစ်ခုအတွက် ထပ်တူညီအောင်လုပ်သော ဆက်တင်များကို ပြင်ရန် အက်ပ်ကို ခွင့်ပြုရန်။ ဥပမာ People အက်ပ်က အကောင့်တစ်ခုနှင့် ထပ်တူညီအောင် လုပ်ဆောင်ခြင်းအား ဖွင့်ရန် သုံးနိုင်သည်။"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ထပ်တူကူးခြင်း ကိန်းဂဏန်းအချက်အလက်များကို ဖတ်ခြင်း"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"အပလီကေးရှင်းအား အကောင့်တစ်ခု၏ ထပ်တူညီအောင် လုပ်ဆောင်မှု အခြေအနေ (ပြီးခဲ့သော အဖြစ်အပျက်၊ ဒေတာ ပမာဏ ပါဝင်မှု များအပါအဝင်)ကို ဖတ်ရှုခွင့် ပြုပါ။"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB သိုလှောင်မှုမှ အချက်အလက်များအား ဖတ်ခြင်း"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD ကဒ်မှ အချက်အလက်များအား ဖတ်ခြင်း"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"အပလီကေးရှင်းအား USB သိုလှောင်မှုပေါ်မှ ဒေတာများ ဖတ်ရှုခွင့်ပြုခြင်း"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"အပလီကေးရှင်းအား အက်စ်ဒီ ကဒ်ပေါ်မှ ဒေတာများ ဖတ်ရှုခွင့်ပြုခြင်း"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USBမှဒေတာအား ပြင် သို့ ဖျက်ရန်"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD ကဒ်ပေါ်မှ အချက်အလက်များအား ပြင်ဆင်ခြင်း သို့ ဖျက်ပစ်ခြင်း"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"အက်ပ်အား USB သိုလှောင်ခန်းသို့ ရေးခွင့် ပြုသည်။"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"အက်ပ်အား SD ကဒ်သို့ ရေးခွင့် ပြုသည်။"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"မျှဝေသိုလှောင်ခန်းမှ အရာများ ဖတ်ခြင်း"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"မျှဝေသိုလှောင်ခန်းမှ အရာများ ဖတ်ရန် ခွင့်ပြုသည်။"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"မျှဝေသိုလှောင်ခန်းမှ အရာများ ပြုပြင်/ဖျက်ခြင်း"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"မျှဝေသိုလှောင်ခန်းမှ အရာများ ရေးခွင့်ပြုသည်။"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP ခေါ်ဆိုမှုများ ခေါ်ရန်/လက်ခံရန်"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP ခေါ်ဆိုမှုများ ခေါ်ရန်နှင့် လက်ခံနိုင်ရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"တယ်လီကွမ် ဆင်းမ် ချိတ်ဆက်မှုများကို မှတ်ပုံတင်ပါ"</string>
@@ -1087,24 +1083,28 @@
<string name="deleteText" msgid="6979668428458199034">"ဖျက်ရန်"</string>
<string name="inputMethod" msgid="1653630062304567879">"ထည့်သွင်းရန်နည်းလမ်း"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"စာတို လုပ်ဆောင်ချက်"</string>
- <string name="email" msgid="4560673117055050403">"အီးမေးလ်"</string>
+ <string name="email" msgid="4560673117055050403">"အီးမေးလ်ပို့ရန်"</string>
<string name="email_desc" msgid="3638665569546416795">"ရွေးထားသည့် လိပ်စာသို့ အီးမေးလ်ပို့ရန်"</string>
<string name="dial" msgid="1253998302767701559">"ခေါ်ဆိုရန်"</string>
<string name="dial_desc" msgid="6573723404985517250">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို ခေါ်ရန်"</string>
- <string name="map" msgid="5441053548030107189">"မြေပုံ"</string>
+ <string name="map" msgid="5441053548030107189">"မြေပုံဖွင့်ရန်"</string>
<string name="map_desc" msgid="1836995341943772348">"ရွေးထားသည့် လိပ်စာကို ရှာဖွေရန်"</string>
<string name="browse" msgid="1245903488306147205">"ဖွင့်ရန်"</string>
<string name="browse_desc" msgid="8220976549618935044">"ရွေးထားသည့် URL ကို ဖွင့်ရန်"</string>
- <string name="sms" msgid="4560537514610063430">"SMS"</string>
+ <string name="sms" msgid="4560537514610063430">"SMS ပို့ရန်"</string>
<string name="sms_desc" msgid="7526588350969638809">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို မက်ဆေ့ဂျ်ပို့ရန်"</string>
<string name="add_contact" msgid="7867066569670597203">"ထည့်ရန်"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"အဆက်အသွယ်များသို့ ထည့်ရန်"</string>
<string name="view_calendar" msgid="979609872939597838">"ကြည့်ရန်"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ရွေးထားသည့် အချိန်ကို ပြက္ခဒိန်တွင် ကြည့်ရန်"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"အချိန်ဇယား"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"အစီအစဉ်ထည့်ရန်"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ရွေးထားသည့်အချိန်အတွက် အစီအစဉ်ပြုလုပ်ရန်"</string>
<string name="view_flight" msgid="7691640491425680214">"ရှာရန်"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"ရွေးထားသည့် လေယာဉ်ခရီးစဉ်ကို ရှာဖွေရန်"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"ရွေးထားသည့် လေယာဉ်ခရီးစဉ်ကို ရှာရန်"</string>
+ <string name="translate" msgid="9218619809342576858">"ဘာသာပြန်ရန်"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ရွေးထားသောစာသားကို ဘာသာပြန်ရန်"</string>
+ <string name="define" msgid="7394820043869954211">"အဓိပ္ပာယ်ဖွင့်ဆိုရန်"</string>
+ <string name="define_desc" msgid="7910883642444919726">"ရွေးထားသောစာသားကို အဓိပ္ပာယ်ဖွင့်ဆိုရန်"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">ကိုက်ညီမှု 1 ခု</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"ပြီးပါပြီ"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB သိုလှောင်မှု အချက်အလက်များ ဖျက်နေစဉ်…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD ကတ် အား ဖျက်နေစဉ်…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"မျှဝေထားသည့် သိုလှောင်ခန်းကို ဖျက်နေသည်…"</string>
<string name="share" msgid="1778686618230011964">"မျှဝေခြင်း"</string>
<string name="find" msgid="4808270900322985960">"ရှာဖွေရန်"</string>
<string name="websearch" msgid="4337157977400211589">"ဝဘ်တွင် ရှာရန်"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS တောင်းဆိုမှုကို USSD တောင်းဆိုမှုအဖြစ် ပြောင်းထားသည်"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"SS တောင်းဆိုမှုအသစ်သို့ ပြောင်းထားသည်"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"အလုပ်ကိုယ်ရေးအချက်အလက်"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"သတိပေးထားသည်"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ချဲ့ရန်"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ခေါက်သိမ်းရန်"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ချဲ့ခြင်းခလုတ်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 5fc75ad..30cd600 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi-anrop | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi-anrop"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi-anrop"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Av"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi er foretrukket"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Først-på-mobil"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Lar appen lese posisjoner fra mediesamlingen din."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Appen <xliff:g id="APP">%s</xliff:g> vil autentisere."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk maskinvare er utilgjengelig"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke gjenkjent"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Deler av fingeravtrykket er registrert. Prøv på nytt."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtrykksensoren er skitten. Rengjør den og prøv på nytt."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Du flyttet fingeren for sakte. Prøv på nytt."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke gjenkjent"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeravtrykket er godkjent"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Ansiktet er autentisert"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Ansiktet er autentisert. Trykk på Bekreft"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Lar appen endre synkroniseringsinnstillingene for en konto. For eksempel kan dette brukes til å synkronisere Personer-appen med en konto."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lese synkroniseringsstatistikk"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Lar appen lese synkroniseringsstatistikk for en konto, inkludert loggen over synkroniseringsaktiviteter og hvor mye data som er synkronisert."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"leser innholdet i USB-lagringen"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"leser innholdet i SD-kortet"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Lar appen lese innhold på USB-lagringen din."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Lar appen lese innhold på SD-kortet ditt."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"endre eller slette innholdet i USB-lagringen"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"endre eller slette innhold i SD-kortet"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Appen tillates å skrive til USB-lagringen."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lar appen skrive til SD-kortet."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"leser innholdet i den delte lagringen din"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Lar appen lese innholdet i den delte lagringen din."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"endre eller slette innholdet i den delte lagringen din"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Lar appen skrive innholdet i den delte lagringen din."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"foreta/motta SIP-anrop"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Tillater at appen utfører og mottar SIP-anrop."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrere nye tilkoblinger for telekom-SIM-kort"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planlegg aktivitet for valgt klokkeslett"</string>
<string name="view_flight" msgid="7691640491425680214">"Spor"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Spor den valgte flyvningen"</string>
+ <string name="translate" msgid="9218619809342576858">"Oversett"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Oversett den valgte teksten"</string>
+ <string name="define" msgid="7394820043869954211">"Definer"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definer den valgte teksten"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det er ikke nok lagringsplass for systemet. Kontrollér at du har 250 MB ledig plass, og start på nytt."</string>
@@ -1265,7 +1265,7 @@
</string-array>
<string name="network_switch_type_name_unknown" msgid="4552612897806660656">"en ukjent nettverkstype"</string>
<string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kan ikke koble til Wi-Fi"</string>
- <string name="wifi_watchdog_network_disabled_detailed" msgid="4917472096696322767">" har en dårlig Internett-tilkobling."</string>
+ <string name="wifi_watchdog_network_disabled_detailed" msgid="4917472096696322767">" har en dårlig internettilkobling."</string>
<string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vil du tillat tilkoblingen?"</string>
<string name="wifi_connect_alert_message" msgid="6451273376815958922">"Appen %1$s vil koble til Wi-Fi-nettverket %2$s"</string>
<string name="wifi_connect_default_application" msgid="7143109390475484319">"En app"</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 kamp</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Ferdig"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Sletter USB-lagring …"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Sletter SD-kort …"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Sletter delt lagring …"</string>
<string name="share" msgid="1778686618230011964">"Del"</string>
<string name="find" msgid="4808270900322985960">"Finn"</string>
<string name="websearch" msgid="4337157977400211589">"Nettsøk"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-forespørsel endret til USSD-forespørsel"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Endret til ny SS-forespørsel"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Arbeidsprofil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Varslet"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Vis"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Skjul"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"slå utvidelse av/på"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index cddd308..188ce8b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wifi कलिङ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi कलिङ"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WiFi कलिङ"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"निष्क्रिय"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi मनपराइयो"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"रूचाइएको मोबाइल"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"यसले अनुप्रयोगलाई तपाईंको मिडिया सङ्ग्रहका स्थानहरू पढ्न दिन्छ।"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"अनुप्रयोग <xliff:g id="APP">%s</xliff:g> ले प्रमाणीकरण गर्न चाहन्छ।"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"पहिचान भएन"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक औठाछाप पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"औठाछाप प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"औँठाछाप सेन्सर फोहोर छ। कृपया सफा गरेर फेरि प्रयास गर्नुहोस्।"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"औंला निकै सुस्त सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"पहिचान भएन"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"अनुहार प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"अनुप्रयोगहरूलाई खाताको लागि सिंक सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ। उदाहरणको लागि, यो खातासँग व्यक्ति अनुप्रयोगको सिंक सक्षम गर्न प्रयोग गर्न सकिन्छ।"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"सिंक तथ्याङ्कहरू पढ्नुहोस्"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"अनुप्रयोगलाई खाताको लागि समीकरणको आँकडा समीकरण घटनाहरूको इतिहास र समीकरण गरिएको डेटाको मापन समेत, पढ्न अनुमति दिन्छ।"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"आफ्नो USB भण्डारणको सामग्रीहरूहरु पढ्नुहोस्"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"आफ्नो SD कार्डको सामग्रीहरूहरु पढ्नुहोस्"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"अनुप्रयोगलाई तपाईंको USB भण्डारणको सामग्री पढ्न अनुमति दिन्छ।"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"अनुप्रयोगलाई तपाईंको SD कार्डको सामग्री पढ्न अनुमति दिन्छ।"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"तपाईँको USB भण्डारणको विषयवस्तुहरूलाई परिमार्जन गर्ने वा मेटाउने"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"तपाईँको SD कार्डको विषयसूची परिमार्जन गर्ने मेट्ने"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB भण्डारणमा लेख्नको लागि अनुप्रयोगलाई अनुमति दिन्छ।"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"अनुप्रयोगलाई SD कार्डमा लेख्न अनुमति दिन्छ।"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"आफ्नो आदान प्रदान गरिएको भण्डारणको सामग्रीहरूहरू पढ्नुहोस्"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"अनुप्रयोगलाई तपाईंको आदान प्रदान गरिएको भण्डारणको सामग्री पढ्न अनुमति दिन्छ।"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"तपाईंको आदान प्रदान गरिएको भण्डारणको विषयवस्तुहरूलाई परिमार्जन गर्नहोस् वा मेटाउनुहोस्"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"अनुप्रयोगलाई तपाईंको आदान प्रदान गरिएको भण्डारणको सामग्री लेख्न अनुमति दिन्छ।"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP कलहरू प्राप्त/बनाउन"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP कलहरू बनाउन र प्राप्त गर्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"नयाँ दूरसंचार सिम जडानहरू दर्ता गर्नुहोस्"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"चयन गरिएको समयका लागि कार्यक्रमको समयतालिका बनाउनुहोस्"</string>
<string name="view_flight" msgid="7691640491425680214">"ट्र्याक गर्नुहोस्"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"चयन गरिएको उडान ट्रयाक गर्नुहोस्"</string>
+ <string name="translate" msgid="9218619809342576858">"अनुवाद गर्नुहोस्"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"चयन गरिएको पाठ अनुवाद गर्नुहोस्"</string>
+ <string name="define" msgid="7394820043869954211">"परिभाषा गर्नुहोस्"</string>
+ <string name="define_desc" msgid="7910883642444919726">"चयन गरिएको पाठ परिभाषा गर्नुहोस्"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"भण्डारण ठाउँ सकिँदै छ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1466,8 +1466,7 @@
<item quantity="one">1 मेल</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"भयो"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB भण्डारण मेटाउँदै…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD कार्ड मेटाउँदै…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"साझेदारी गरिएको भण्डारण मेट्दै…"</string>
<string name="share" msgid="1778686618230011964">"साझेदारी गर्नुहोस्"</string>
<string name="find" msgid="4808270900322985960">"पत्ता लगाउनुहोस्"</string>
<string name="websearch" msgid="4337157977400211589">"वेब खोजी"</string>
@@ -1839,8 +1838,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS अनुरोधलाई USSD अनुरोधमा परिवर्तन गरियो"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"नयाँ SS अनुरोधमा परिवर्तन गरियो"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"कार्य प्रोफाइल"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"सतर्कता गरियो"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"विस्तृत गर्नुहोस्"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"संक्षिप्त गर्नुहोस्"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"विस्तारलाई टगल गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c1883fa..2ff9fde 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wifi van <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Bellen via wifi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi van <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Bellen via wifi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wifi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Bellen via wifi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Uit"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Voorkeur voor wifi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Voorkeur voor mobiel"</string>
@@ -251,7 +247,7 @@
<string name="global_action_settings" msgid="1756531602592545966">"Instellingen"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Helpen"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Spraakassistent"</string>
- <string name="global_action_lockdown" msgid="1099326950891078929">"Afgesloten"</string>
+ <string name="global_action_lockdown" msgid="1099326950891078929">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
<string name="notification_hidden_text" msgid="6351207030447943784">"Nieuwe melding"</string>
<string name="notification_channel_virtual_keyboard" msgid="6969925135507955575">"Virtueel toetsenbord"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Hiermee sta je de app toe locaties van je mediacollectie te bekijken."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"App <xliff:g id="APP">%s</xliff:g> wil een verificatie uitvoeren."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrische hardware niet beschikbaar"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Niet herkend"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelijke vingerafdruk gedetecteerd. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"De vingerafdruksensor moet worden schoongemaakt. Probeer het daarna opnieuw."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vinger te langzaam bewogen. Probeer het opnieuw."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Niet herkend"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Vingerafdruk geverifieerd"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Gezicht geverifieerd"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Gezicht geverifieerd. Druk op Bevestigen."</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Hiermee kan een app de synchronisatie-instellingen aanpassen voor een account. Deze toestemming kan bijvoorbeeld worden gebruikt om synchronisatie van de app Personen in te schakelen voor een account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"synchronisatiestatistieken lezen"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Hiermee kan een app de synchronisatiestatistieken voor een account lezen, inclusief de geschiedenis van synchronisatie-activiteiten en hoeveel gegevens zijn gesynchroniseerd."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"de content van je USB-opslag lezen"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"de content van je SD-kaart lezen"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"De app toestaan de content van je USB-opslag te lezen."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"De app toestaan de content van je SD-kaart te lezen."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"de content van je USB-opslag aanpassen of verwijderen"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"de content van je SD-kaart aanpassen of verwijderen"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Hiermee kan de app schrijven naar de USB-opslag."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Hiermee kan de app schrijven naar de SD-kaart."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"de content van je gedeelde opslag lezen"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Hiermee kan de app de content van je gedeelde opslag lezen."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"de content van je gedeelde opslag aanpassen of verwijderen"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Hiermee kan de app de content van je gedeelde opslag schrijven."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"Bellen of gebeld worden via SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Toestaan dat de app belt en gebeld wordt via SIP"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"nieuwe telecom-sim-verbindingen registreren"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Evenement plannen voor geselecteerde tijd"</string>
<string name="view_flight" msgid="7691640491425680214">"Volgen"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Geselecteerde vlucht tonen"</string>
+ <string name="translate" msgid="9218619809342576858">"Vertalen"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Geselecteerde tekst vertalen"</string>
+ <string name="define" msgid="7394820043869954211">"Definiëren"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Geselecteerde tekst definiëren"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 overeenkomst</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Gereed"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB-opslag wissen..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD-kaart wissen..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Gedeelde opslag wissen…"</string>
<string name="share" msgid="1778686618230011964">"Delen"</string>
<string name="find" msgid="4808270900322985960">"Vinden"</string>
<string name="websearch" msgid="4337157977400211589">"Google Zoeken"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-verzoek gewijzigd in USSD-verzoek"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Gewijzigd in nieuw SS-verzoek"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Werkprofiel"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Gemeld"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Uitvouwen"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Samenvouwen"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"uitvouwen in-/uitschakelen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 59c689b..f1c7baf 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> ୱାଇ-ଫାଇ"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"ୱାଇଫାଇ କଲିଂ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"ୱାଇ-ଫାଇ କଲିଂ"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"ୱାଇ-ଫାଇ"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"ୱାଇଫାଇ କଲିଂ"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ଅଫ୍"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"ପସନ୍ଦ କରାଯାଇଥିବା ୱାଇ-ଫାଇ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ପସନ୍ଦର ମୋବାଇଲ୍"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ଆପଣଙ୍କ ମିଡିଆ ସଂଗ୍ରହ ଠାରୁ ଅବସ୍ଥାନଗୁଡିକୁ ପଢିବାକୁ ଆପ୍ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"ଆପ୍ଲିକେସନ୍ <xliff:g id="APP">%s</xliff:g> ପ୍ରମାଣିତକୃତ କରିବାକୁ ଚାହୁଁଛି।"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ବାୟୋମେଟ୍ରିକ୍ ହାର୍ଡୱେର୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଂଶିକ ଚିହ୍ନଟ ହେଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରୋସେସ୍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନ୍ସର୍ ମଇଳା ହୋଇଯାଇଛି। ଦୟାକରି ସଫା କରନ୍ତୁ ଓ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ଆଙ୍ଗୁଠି ଖୁବ୍ ଧୀରେ ନିଆଗଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରମାଣୀକୃତ ହେଲା"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି, ଦୟାକରି ସୁନିଶ୍ଚିତ ଦବାନ୍ତୁ"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ଆପ୍କୁ ଏକ ଆକାଉଣ୍ଟ ପାଇଁ ସିଙ୍କ ସେଟିଙ୍ଗ ସଂଶୋଧନ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଉଦାହରଣସ୍ୱରୂପ, ଏହାର ବ୍ୟବହାର କୌଣସି ଆକାଉଣ୍ଟରେ ଥିବା ଲୋକଙ୍କ ଆପ୍ ସହିତ ସିଙ୍କ କରିବାକୁ ସକ୍ଷମ ହୋଇଥାଏ।"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ସିଙ୍କ୍ ପରିସଂଖ୍ୟାନକୁ ପଢ଼ନ୍ତୁ।"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ସିଙ୍କ କାର୍ଯ୍ୟର ହିଷ୍ଟୋରୀ ତଥା କେତେ ଡାଟା ସିଙ୍କ କରାଯାଇଛି, ସେଗୁଡ଼ିକ ଅନ୍ତର୍ଭୁକ୍ତ କରି, ଏକ ଆକାଉଣ୍ଟର ସିଙ୍କ ଅବସ୍ଥା ପଢ଼ିବା ପାଇଁ ଗୋଟିଏ ଆପ୍କୁ ଅନୁମତି ଦିଏ।"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ନିଜ USB ଷ୍ଟୋରେଜ୍ର କଣ୍ଟେଣ୍ଟ ପଢ଼ନ୍ତୁ"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ନିଜ SD କାର୍ଡର କଣ୍ଟେଣ୍ଟ ପଢ଼ନ୍ତୁ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"USB ଷ୍ଟୋରେଜ୍ରେ ଥିବା କଣ୍ଟେଣ୍ଟ ପଢିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"ଆପଣଙ୍କ SD କାର୍ଡରେ ଥିବା କଣ୍ଟେଣ୍ଟ ପଢ଼ିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ଆପଣଙ୍କ USB ଷ୍ଟୋରେଜ୍ର କଣ୍ଟେଣ୍ଟ ସଂଶୋଧନ କରନ୍ତୁ କିମ୍ବା ଡିଲିଟ୍ କରନ୍ତୁ"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ନିଜ SD କାର୍ଡର କଣ୍ଟେଣ୍ଟ ସଂଶୋଧନ କିମ୍ବା ଡିଲିଟ୍ କରନ୍ତୁ"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ଆପ୍କୁ USB ଷ୍ଟୋରେଜରେ ଲେଖିବାକୁ ଦେଇଥାଏ।"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"ଆପ୍କୁ SD କାର୍ଡରେ ଲେଖିବାକୁ ଦେଇଥାଏ।"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ଆପଣଙ୍କର ସେୟାର୍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍ର ବିଷୟବସ୍ତୁ ପଢ଼ନ୍ତୁ"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"ଆପଣଙ୍କର ସେୟାର୍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍ରେ ଥିବା ବିଷୟବସ୍ତୁ ପଢିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ଆପଣଙ୍କତ ସେୟାର୍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍ର ବିଷୟବସ୍ତୁ ସଂଶୋଧନ କିମ୍ବା ଡିଲିଟ୍ କରନ୍ତୁ"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"ଆପଣଙ୍କର ସେୟାର୍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍ର ବିଷୟବସ୍ତୁ ଲେଖିବାକୁ ଅନୁମତି କରିଥାଏ।"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP କଲ୍ କରନ୍ତୁ ଏବଂ ଗ୍ରହଣ କରନ୍ତୁ"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ଆପ୍କୁ SIP କଲ୍ କରିବାକୁ ଏବଂ ପାଇବାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ନୂତନ ଦୂରସଂଚାର ସିମ୍ ସଂଯୋଗ ନଥିଭୁକ୍ତ କରନ୍ତୁ"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ବାଛି ନେଇଥିବା ସମୟଟିରେ ଇଭେଣ୍ଟ ସେଟ୍ କରନ୍ତୁ"</string>
<string name="view_flight" msgid="7691640491425680214">"ଟ୍ରାକ୍ କରନ୍ତୁ"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ବାଛି ନେଇଥିବା ଫ୍ଲାଇଟ୍କୁ ଟ୍ରାକ୍ କରନ୍ତୁ"</string>
+ <string name="translate" msgid="9218619809342576858">"ଅନୁବାଦ"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ବଛାଯାଇଥିବା ପାଠର ଅନୁବାଦ କରନ୍ତୁ"</string>
+ <string name="define" msgid="7394820043869954211">"ନିର୍ଦ୍ଧିଷ୍ଟ କରନ୍ତୁ"</string>
+ <string name="define_desc" msgid="7910883642444919726">"ବଛାଯାଇଥିବା ପାଠ୍ୟ ନିର୍ଦ୍ଧିଷ୍ଟ କରନ୍ତୁ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ଷ୍ଟୋରେଜ୍ ସ୍ପେସ୍ ଶେଷ ହେବାରେ ଲାଗିଛି"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"କିଛି ସିଷ୍ଟମ ଫଙ୍କଶନ୍ କାମ କରିନପାରେ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।"</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">1ଟି ମେଳ</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"ହୋଇଗଲା"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB ଷ୍ଟୋରେଜ୍ ଲିଭାଯାଉଛି…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD କାର୍ଡ ଲିଭାଯାଉଛି…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"ସେୟାର୍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍ ଲିଭାଉଛି…"</string>
<string name="share" msgid="1778686618230011964">"ଶେୟାର୍"</string>
<string name="find" msgid="4808270900322985960">"ଖୋଜନ୍ତୁ"</string>
<string name="websearch" msgid="4337157977400211589">"ୱେବ୍ ସର୍ଚ୍ଚ"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS ଅନୁରୋଧ, USSD ଅନୁରୋଧକୁ ପରିବର୍ତ୍ତନ ହେଲା"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"ନୂତନ SS ଅନୁରୋଧରେ ପରିବର୍ତ୍ତନ ହେଲା"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"ଆଲର୍ଟ କରାଯାଇଛି"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ବଢ଼ାନ୍ତୁ"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ଛୋଟ କରନ୍ତୁ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ଟୋଗଲ୍ ସମ୍ପ୍ରସାରଣ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 0bbe94d..e3fe527 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> ਵਾਈ-ਫਾਈ"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"ਵਾਈ-ਫਾਈ ਕਾਲਿੰਗ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"ਵਾਈ-ਫਾਈ ਕਾਲਿੰਗ"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"ਵਾਈ-ਫਾਈ"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"ਵਾਈ-ਫਾਈ ਕਾਲਿੰਗ"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ਬੰਦ"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"ਤਰਜੀਹੀ ਵਾਈ-ਫਾਈ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ਮੋਬਾਈਲ ਨੂੰ ਤਰਜੀਹ ਹੈ"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਮੀਡੀਆ ਸੰਗ੍ਰਹਿ ਦੇ ਟਿਕਾਣਿਆਂ ਨੂੰ ਪੜ੍ਹਨ ਦਿੰਦੀ ਹੈ।"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"ਐਪਲੀਕੇਸ਼ਨ <xliff:g id="APP">%s</xliff:g> ਪ੍ਰਮਾਣੀਕਰਨ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ।"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ਅਧੂਰਾ ਫਿੰਗਰਪ੍ਰਿਟ ਮਿਲਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕਰ ਸਕਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੰਵੇਦਕ ਗੰਦਾ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਇਸਨੂੰ ਸਾਫ਼ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ਉਂਗਲ ਕਾਫ਼ੀ ਹੌਲੀ ਮੂਵ ਹੋਈ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ਐਪ ਨੂੰ ਇੱਕ ਖਾਤੇ ਲਈ ਸਮਕਾਲੀਕਰਨ ਸੈਟਿੰਗਾਂ ਸੋਧਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸਦੀ ਵਰਤੋਂ ਕਿਸੇ ਖਾਤੇ ਨਾਲ People ਐਪ ਦਾ ਸਮਕਾਲੀਕਰਨ ਚਾਲੂ ਕਰਨ ਲਈ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ਸਿੰਕ ਅੰਕੜੇ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ਐਪ ਨੂੰ ਇੱਕ ਖਾਤੇ ਲਈ ਸਮਕਾਲੀਕਰਨ ਸਥਿਤੀ ਪੜ੍ਹਨ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ, ਇਸ ਵਿੱਚ ਸਮਕਾਲੀਕਰਨ ਵਰਤਾਰਿਆਂ ਦਾ ਇਤਿਹਾਸ ਅਤੇ ਕਿੰਨੇ ਡਾਟਾ ਦਾ ਸਮਕਾਲੀਕਿਰਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਵੀ ਸ਼ਾਮਲ ਹੈ।"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB ਸਟੋਰੇਜ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਪੜ੍ਹੋ"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ਆਪਣੇ SD ਕਾਰਡ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਪੜ੍ਹੋ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"ਐਪ ਨੂੰ ਆਪਣੀ USB ਸਟੋਰੇਜ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ SD ਕਾਰਡ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ਆਪਣੀ USB ਸਟੋਰੇਜ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਸੰਸ਼ੋਧਿਤ ਕਰੋ ਜਾਂ ਮਿਟਾਓ"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ਆਪਣੇ SD ਕਾਰਡ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਸੰਸ਼ੋਧਿਤ ਕਰੋ ਜਾਂ ਮਿਟਾਓ"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ਐਪ ਨੂੰ USB ਸਟੋਰੇਜ ਲਿਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"ਐਪ ਨੂੰ SD ਕਾਰਡ ਲਿਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ਸਮੱਗਰੀਆਂ ਪੜ੍ਹੋ"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"ਐਪ ਨੂੰ ਸਮੱਗਰੀਆਂ ਪੜ੍ਹਨ ਦਿੰਦੀ ਹੈ।"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ਸਮੱਗਰੀਆਂ ਦਾ ਸੰਸ਼ੋਧਨ ਕਰੋ ਜਾਂ ਮਿਟਾਓ"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"ਐਪ ਨੂੰ ਸਮੱਗਰੀਆਂ ਲਿਖਣ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP ਕਾਲਾਂ ਕਰੋ/ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ਐਪ ਨੂੰ SIP ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ਨਵਾਂ ਟੈਲੀਕੌਮ SIM ਕਨੈਕਸ਼ਨ ਰਜਿਸਟਰ ਕਰੋ"</string>
@@ -1087,7 +1083,7 @@
<string name="deleteText" msgid="6979668428458199034">"ਮਿਟਾਓ"</string>
<string name="inputMethod" msgid="1653630062304567879">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"ਟੈਕਸਟ ਕਿਰਿਆਵਾਂ"</string>
- <string name="email" msgid="4560673117055050403">"ਈਮੇਲ ਕਰੋ"</string>
+ <string name="email" msgid="4560673117055050403">"ਈਮੇਲ ਖੋਲ੍ਹੋ"</string>
<string name="email_desc" msgid="3638665569546416795">"ਚੁਣੇ ਹੋਏ ਪਤੇ \'ਤੇ ਈਮੇਲ ਭੇਜੋ"</string>
<string name="dial" msgid="1253998302767701559">"ਕਾਲ ਕਰੋ"</string>
<string name="dial_desc" msgid="6573723404985517250">"ਚੁਣੇ ਗਏ ਫ਼ੋਨ ਨੰਬਰ \'ਤੇ ਕਾਲ ਕਰੋ"</string>
@@ -1101,10 +1097,14 @@
<string name="add_contact_desc" msgid="4830217847004590345">"ਸੰਪਰਕਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="view_calendar" msgid="979609872939597838">"ਦੇਖੋ"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ਕੈਲੰਡਰ ਵਿੱਚ ਚੁਣਿਆ ਗਿਆ ਸਮਾਂ ਦੇਖੋ"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"ਸਮਾਂ-ਸੂਚੀ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ਸਮਾਂ-ਸੂਚੀ ਬਣਾਓ"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ਚੁਣੇ ਗਏ ਸਮੇਂ ਲਈ ਇਵੈਂਟ ਦੀ ਸਮਾਂ-ਸੂਚੀ ਬਣਾਓ"</string>
<string name="view_flight" msgid="7691640491425680214">"ਟਰੈਕ ਕਰੋ"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ਚੁਣੀ ਗਈ ਉਡਾਣ ਨੂੰ ਟਰੈਕ ਕਰੋ"</string>
+ <string name="translate" msgid="9218619809342576858">"ਅਨੁਵਾਦ ਕਰੋ"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ਚੋਣਵੀਂ ਲਿਖਤ ਦਾ ਅਨੁਵਾਦ ਕਰੋ"</string>
+ <string name="define" msgid="7394820043869954211">"ਪਰਿਭਾਸ਼ਾ ਦਿਓ"</string>
+ <string name="define_desc" msgid="7910883642444919726">"ਚੋਣਵੀਂ ਲਿਖਤ ਦਾ ਅਨੁਵਾਦ ਕਰੋ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
@@ -1461,8 +1461,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> </item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"ਹੋ ਗਿਆ"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB ਸਟੋਰੇਜ ਮਿਟਾ ਰਿਹਾ ਹੈ…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD ਕਾਰਡ ਮਿਟਾ ਰਿਹਾ ਹੈ…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"ਸਾਂਝੀ ਕੀਤੀ ਸਟੋਰੇਜ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…"</string>
<string name="share" msgid="1778686618230011964">"ਸਾਂਝਾ ਕਰੋ"</string>
<string name="find" msgid="4808270900322985960">"ਲੱਭੋ"</string>
<string name="websearch" msgid="4337157977400211589">"ਵੈੱਬ ਖੋਜ"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS ਬੇਨਤੀ ਨੂੰ USSD ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"ਨਵੀਂ SS ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"ਸੁਚੇਤਨਾਵਾਂ"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ਸੁੰਗੇੜੋ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ਟੌਗਲ ਵਿਸਤਾਰ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index f36edf4..85303ee 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g>, Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Połączenia przez Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g>, VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Połączenia przez Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Połączenia przez Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Wył."</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferuj Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferowane komórkowe"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Zezwala aplikacji na odczytywanie lokalizacji z kolekcji multimediów."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacja <xliff:g id="APP">%s</xliff:g> wymaga uwierzytelnienia."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Sprzęt biometryczny niedostępny"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nie rozpoznano"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Odcisk palca został odczytany tylko częściowo. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Czytnik linii papilarnych jest zabrudzony. Wyczyść go i spróbuj ponownie."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Palec został obrócony zbyt wolno. Spróbuj ponownie."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nie rozpoznano"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Uwierzytelniono odciskiem palca"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Twarz rozpoznana"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Twarz rozpoznana, kliknij Potwierdź"</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Pozwala aplikacji na modyfikowanie ustawień synchronizacji z kontem. Tego uprawnienia można użyć np. do włączenia synchronizacji z kontem aplikacji Ludzie."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"czytanie statystyk dotyczących synchronizowania"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Pozwala aplikacji na czytanie statystyk synchronizacji konta, w tym historii zdarzeń synchronizacji oraz ilości zsynchronizowanych danych."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"odczytywanie zawartości pamięci USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"odczytywanie zawartości karty SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Pozwala aplikacji na odczyt Twojej pamięci USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Pozwala aplikacji na odczyt Twojej karty SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modyfikowanie i usuwanie zawartości pamięci USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modyfikowanie i usuwanie zawartości karty SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Pozwala aplikacji na zapis w pamięci USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pozwala aplikacji na zapis na karcie SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"odczyt zawartości pamięci współdzielonej"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Zezwala aplikacji na odczyt zawartości pamięci współdzielonej."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modyfikowanie i usuwanie zawartości pamięci współdzielonej"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Zezwala aplikacji na zapis zawartości pamięci współdzielonej."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"wykonywanie/odbieranie połączeń SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Pozwala aplikacji na wykonywanie i odbieranie połączeń SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"rejestrowanie nowych połączeń telekomunikacyjnych SIM"</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Zaplanuj wydarzenie na wybraną godzinę"</string>
<string name="view_flight" msgid="7691640491425680214">"Utwór"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Śledź wybrany lot"</string>
+ <string name="translate" msgid="9218619809342576858">"Tłumacz"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Przetłumacz zaznaczony tekst"</string>
+ <string name="define" msgid="7394820043869954211">"Pokaż definicję"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Pokaż definicję zaznaczonego tekstu"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string>
@@ -1506,8 +1506,7 @@
<item quantity="one">1 dopasowanie</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Gotowe"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Czyszczenie nośnika USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Czyszczenie karty SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Kasuję dane z pamięci współdzielonej…"</string>
<string name="share" msgid="1778686618230011964">"Udostępnij"</string>
<string name="find" msgid="4808270900322985960">"Znajdź"</string>
<string name="websearch" msgid="4337157977400211589">"Wyszukiwarka"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Żądanie SS zmienione na żądanie USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Zmieniono na nowe żądanie SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil służbowy"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alert"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Rozwiń"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Zwiń"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"przełącz rozwijanie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 05bb1ed..1cbcf0f 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Chamada no Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi de <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Chamada no Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Chamada no Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desativado"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi preferido"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferência pela rede móvel"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que o app leia os locais na sua coleção de mídias."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"O app <xliff:g id="APP">%s</xliff:g> está solicitando autenticação."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"O movimento do dedo está muito lento. Tente novamente."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Impressão digital autenticada"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Rosto autenticado"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Rosto autenticado, pressione \"Confirmar\""</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permite que o app modifique as configurações de sincronização de uma conta. Por exemplo, pode ser usado para ativar a sincronização do app People com uma conta."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ler estatísticas de sincronização"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permite que um app acesse as estatísticas de sincronização de uma conta, incluindo a história dos eventos de sincronização e a quantidade de dados sincronizados."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ler conteúdo do armaz. USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ler conteúdo do cartão SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Perm. que app leia cartão SD."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite que o app leia o conteúdo do cartão SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar ou excluir conteúdo do armazenamento USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar ou excluir o conteúdo do cartão SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite gravar no armaz. USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que o app grave em seu cartão SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ler conteúdo do armaz. comp."</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permite que o app leia o conteúdo do armaz. compartilhado."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"alterar ou excluir conteúdo do armaz. compartilhado"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Permite que o app grave o conteúdo do armaz. compartilhado."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"fazer/receber chamadas SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permite que o app faça e receba chamadas SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrar novas conexões SIM de telecomunicações"</string>
@@ -653,20 +649,20 @@
<string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras para senha"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Controla o tamanho e os caracteres permitidos nos PINs e nas senhas do bloqueio de tela."</string>
<string name="policylab_watchLogin" msgid="5091404125971980158">"Monitorar tentativas de desbloqueio de tela"</string>
- <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Monitorar quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloquear o tablet ou apagar todos os dados do tablet se a senha for digitada incorretamente muitas vezes."</string>
+ <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia a TV ou apagar todos os dados dela se muitas senhas incorretas forem digitadas."</string>
- <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Monitorar quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloquear o telefone ou apagar todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o tablet ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia a TV ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o smartphone ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policylab_resetPassword" msgid="4934707632423915395">"Alterar o bloqueio de tela"</string>
<string name="policydesc_resetPassword" msgid="1278323891710619128">"Altera o bloqueio de tela."</string>
<string name="policylab_forceLock" msgid="2274085384704248431">"Bloquear a tela"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"Controlar como e quando a tela é bloqueada."</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"Controla como e quando a tela é bloqueada."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Apague os dados do tablet sem aviso redefinindo a configuração original."</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Apaga dados da TV sem aviso, fazendo uma redefinição para configuração original."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apagar os dados do telefone sem aviso redefinindo a configuração original."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apaga os dados do telefone sem aviso redefinindo a configuração original."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Limpar dados do usuário"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Limpa os dados do usuário neste tablet sem aviso prévio."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Limpa os dados do usuário nesta TV sem aviso prévio."</string>
@@ -676,9 +672,9 @@
<string name="policylab_expirePassword" msgid="5610055012328825874">"Definir expiração da senha de bloqueio de tela"</string>
<string name="policydesc_expirePassword" msgid="5367525762204416046">"Altera a frequência com que o PIN, a senha ou o padrão do bloqueio de tela deve ser alterado."</string>
<string name="policylab_encryptedStorage" msgid="8901326199909132915">"Definir criptografia de armazenamento"</string>
- <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exija que os dados armazenados do app sejam criptografados."</string>
+ <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exige que os dados armazenados do app sejam criptografados."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Desativar câmeras"</string>
- <string name="policydesc_disableCamera" msgid="2306349042834754597">"Impeça o uso de todas as câmeras do dispositivo."</string>
+ <string name="policydesc_disableCamera" msgid="2306349042834754597">"Impede o uso de todas as câmeras do dispositivo."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Desativar recursos bloq. de tela"</string>
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Impede o uso de alguns recursos do bloqueio de tela."</string>
<string-array name="phoneTypes">
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programar evento para a hora selecionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Rastrear"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Rastrear voo selecionado"</string>
+ <string name="translate" msgid="9218619809342576858">"Traduzir"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduzir texto selecionado"</string>
+ <string name="define" msgid="7394820043869954211">"Definir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definir texto selecionado"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Concluído"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Apagando o armazenamento USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Apagando cartão SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Limpando armazenamento compartilhado…"</string>
<string name="share" msgid="1778686618230011964">"Compartilhar"</string>
<string name="find" msgid="4808270900322985960">"Localizar"</string>
<string name="websearch" msgid="4337157977400211589">"Pesquisa Google na Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Solicitação SS alterada para solicitação USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Alterada para uma nova solicitação SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabalho"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alertado"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expandir"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Recolher"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"alternar expansão"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index abb0023..6ad4ec3 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Chamadas Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Chamadas Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Chamadas Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desativado"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Rede Wi-Fi preferida"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferência pela rede móvel"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que a aplicação leia as localizações a partir da sua coleção de multimédia."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"A aplicação <xliff:g id="APP">%s</xliff:g> pretende uma autenticação"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível."</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido."</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital detetada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressões digitais está sujo. Limpe-o e tente novamente."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Moveu o dedo demasiado lentamente. Tente novamente."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido."</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"A impressão digital foi autenticada."</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Rosto autenticado."</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Rosto autenticado. Prima Confirmar."</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permite que uma aplicação modifique as definições de sincronização de uma conta. Por exemplo, pode ser utilizada para ativar a sincronização da aplicação Pessoas com uma conta."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ler estatísticas de sincronização"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permite que uma aplicação leia o estado de sincronização de uma conta, incluindo o histórico de eventos de sincronização e a quantidade de dados sincronizados."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ler os conteúdos da memória USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ler os conteúdos do cartão SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permite que a aplicação leia conteúdos da memória USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite que a aplicação leia conteúdos do cartão SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar ou eliminar os conteúdos da memória USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar ou eliminar os conteúdos do cartão SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite que a aplicação escreva na unidade de armazenamento USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que a aplicação escreva no cartão SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ler os conteúdos do armazen. partilhado"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permite que a aplicação leia conteúdos do armazenamento partilhado."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modif./elim. os conteúdos do armazenam. partilhado"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Permite que a apl. escreva conteúd. do armazen. partilhado."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"efetuar/receber chamadas SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permite que a aplicação efetue e receba chamadas SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registar novas ligações SIM de telecomunicações"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Agendar um evento para a hora selecionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Monitorizar"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Monitorizar o voo selecionado"</string>
+ <string name="translate" msgid="9218619809342576858">"Traduzir"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduzir o texto selecionado"</string>
+ <string name="define" msgid="7394820043869954211">"Definir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definir o texto selecionado"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 correspondência</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Concluído"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"A apagar memória de armazenamento USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"A apagar cartão SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"A apagar o armazenamento partilhado…"</string>
<string name="share" msgid="1778686618230011964">"Partilhar"</string>
<string name="find" msgid="4808270900322985960">"Localizar"</string>
<string name="websearch" msgid="4337157977400211589">"Pesquisar na Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"O pedido SS foi alterado para um novo pedido USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Foi alterado para um novo pedido SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabalho"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alertado"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expandir"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Reduzir"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ativar/desativar expansão"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 05bb1ed..1cbcf0f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Chamada no Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi de <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Chamada no Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Chamada no Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desativado"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi preferido"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferência pela rede móvel"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que o app leia os locais na sua coleção de mídias."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"O app <xliff:g id="APP">%s</xliff:g> está solicitando autenticação."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"O movimento do dedo está muito lento. Tente novamente."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Impressão digital autenticada"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Rosto autenticado"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Rosto autenticado, pressione \"Confirmar\""</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permite que o app modifique as configurações de sincronização de uma conta. Por exemplo, pode ser usado para ativar a sincronização do app People com uma conta."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ler estatísticas de sincronização"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permite que um app acesse as estatísticas de sincronização de uma conta, incluindo a história dos eventos de sincronização e a quantidade de dados sincronizados."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ler conteúdo do armaz. USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ler conteúdo do cartão SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Perm. que app leia cartão SD."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite que o app leia o conteúdo do cartão SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar ou excluir conteúdo do armazenamento USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar ou excluir o conteúdo do cartão SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite gravar no armaz. USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que o app grave em seu cartão SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ler conteúdo do armaz. comp."</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permite que o app leia o conteúdo do armaz. compartilhado."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"alterar ou excluir conteúdo do armaz. compartilhado"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Permite que o app grave o conteúdo do armaz. compartilhado."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"fazer/receber chamadas SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permite que o app faça e receba chamadas SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrar novas conexões SIM de telecomunicações"</string>
@@ -653,20 +649,20 @@
<string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras para senha"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Controla o tamanho e os caracteres permitidos nos PINs e nas senhas do bloqueio de tela."</string>
<string name="policylab_watchLogin" msgid="5091404125971980158">"Monitorar tentativas de desbloqueio de tela"</string>
- <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Monitorar quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloquear o tablet ou apagar todos os dados do tablet se a senha for digitada incorretamente muitas vezes."</string>
+ <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia a TV ou apagar todos os dados dela se muitas senhas incorretas forem digitadas."</string>
- <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Monitorar quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloquear o telefone ou apagar todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o tablet ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia a TV ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o smartphone ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policylab_resetPassword" msgid="4934707632423915395">"Alterar o bloqueio de tela"</string>
<string name="policydesc_resetPassword" msgid="1278323891710619128">"Altera o bloqueio de tela."</string>
<string name="policylab_forceLock" msgid="2274085384704248431">"Bloquear a tela"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"Controlar como e quando a tela é bloqueada."</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"Controla como e quando a tela é bloqueada."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Apague os dados do tablet sem aviso redefinindo a configuração original."</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Apaga dados da TV sem aviso, fazendo uma redefinição para configuração original."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apagar os dados do telefone sem aviso redefinindo a configuração original."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apaga os dados do telefone sem aviso redefinindo a configuração original."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Limpar dados do usuário"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Limpa os dados do usuário neste tablet sem aviso prévio."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Limpa os dados do usuário nesta TV sem aviso prévio."</string>
@@ -676,9 +672,9 @@
<string name="policylab_expirePassword" msgid="5610055012328825874">"Definir expiração da senha de bloqueio de tela"</string>
<string name="policydesc_expirePassword" msgid="5367525762204416046">"Altera a frequência com que o PIN, a senha ou o padrão do bloqueio de tela deve ser alterado."</string>
<string name="policylab_encryptedStorage" msgid="8901326199909132915">"Definir criptografia de armazenamento"</string>
- <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exija que os dados armazenados do app sejam criptografados."</string>
+ <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exige que os dados armazenados do app sejam criptografados."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Desativar câmeras"</string>
- <string name="policydesc_disableCamera" msgid="2306349042834754597">"Impeça o uso de todas as câmeras do dispositivo."</string>
+ <string name="policydesc_disableCamera" msgid="2306349042834754597">"Impede o uso de todas as câmeras do dispositivo."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Desativar recursos bloq. de tela"</string>
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Impede o uso de alguns recursos do bloqueio de tela."</string>
<string-array name="phoneTypes">
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programar evento para a hora selecionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Rastrear"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Rastrear voo selecionado"</string>
+ <string name="translate" msgid="9218619809342576858">"Traduzir"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduzir texto selecionado"</string>
+ <string name="define" msgid="7394820043869954211">"Definir"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definir texto selecionado"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Concluído"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Apagando o armazenamento USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Apagando cartão SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Limpando armazenamento compartilhado…"</string>
<string name="share" msgid="1778686618230011964">"Compartilhar"</string>
<string name="find" msgid="4808270900322985960">"Localizar"</string>
<string name="websearch" msgid="4337157977400211589">"Pesquisa Google na Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Solicitação SS alterada para solicitação USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Alterada para uma nova solicitação SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabalho"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Alertado"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Expandir"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Recolher"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"alternar expansão"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 40812fd..dca0510 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -137,14 +137,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Apelare prin Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Apelare prin Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Apelare prin Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Dezactivată"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Se preferă conexiunea Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Se preferă datele mobile"</string>
@@ -536,6 +532,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite aplicației să citească locațiile din colecția dvs. media."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplicația <xliff:g id="APP">%s</xliff:g> dorește să se autentifice."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometric indisponibil"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nu este recunoscut"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"S-a detectat parțial amprenta. Încercați din nou."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Amprenta nu a putut fi procesată. Încercați din nou."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzorul pentru amprente este murdar. Curățați-l și încercați din nou."</string>
@@ -543,7 +544,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Ați mișcat degetul prea lent. Încercați din nou."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nu este recunoscut"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Amprentă autentificată"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Chip autentificat"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Chip autentificat, apăsați Confirmați"</string>
@@ -599,14 +599,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permite unei aplicații să modifice setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate activa sincronizarea aplicației Persoane cu un anumit cont."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"citire statistici privind sincronizarea"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Permite unei aplicații să citească statisticile de sincronizare ale unui cont, inclusiv istoricul evenimentelor de sincronizare și volumul datelor sincronizate."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"citește conținutul stocării USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"citește conținutul cardului SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Permite aplic. citirea conținutului stoc. USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Permite aplicației citirea conținutul cardului SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifică sau șterge conținutul stocării USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifică sau șterge conținutul cardului SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite scriere în stoc. USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite aplicației să scrie pe cardul SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"să citească conținutul spațiului de stocare comun"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Permite aplicației citirea conținutului spațiului de stocare comun."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"să modifice sau să șteargă conținutul spațiului de stocare comun"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Permite aplicației scrierea conținutul spațiului de stocare comun."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"efectuarea/primirea apelurilor SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Permite aplicației să efectueze și să primească apeluri SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"se înregistrează conexiuni noi de telecomunicații pentru SIM"</string>
@@ -1125,6 +1121,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programați evenimentul pentru momentul selectat"</string>
<string name="view_flight" msgid="7691640491425680214">"Urmăriți"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Urmăriți zborul selectat"</string>
+ <string name="translate" msgid="9218619809342576858">"Traduceți"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traduceți textul selectat"</string>
+ <string name="define" msgid="7394820043869954211">"Definiți"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definiți textul selectat"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spațiul de stocare aproape ocupat"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
@@ -1483,8 +1483,7 @@
<item quantity="one">Un rezultat</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Terminat"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Se șterge stocarea USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Se șterge cardul SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Se șterge spațiul de stocare distribuit..."</string>
<string name="share" msgid="1778686618230011964">"Distribuiți"</string>
<string name="find" msgid="4808270900322985960">"Găsiți"</string>
<string name="websearch" msgid="4337157977400211589">"Căutare pe web"</string>
@@ -1866,8 +1865,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Solicitarea SS a fost schimbată cu o solicitare USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Schimbat cu o solicitare SS nouă"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil de serviciu"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Notificat"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Extindeți"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Restrângeți"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"extindeți/restrângeți"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 766a68b..8278c28 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Звонки по Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Звонки по Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Звонки по Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Отключено"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Приоритет Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Приоритет мобильного Интернета"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Приложение получит доступ к геоданным в вашей медиаколлекции."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" запрашивает аутентификацию"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрическое оборудование недоступно"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не распознано"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Отсканирована только часть пальца. Повторите попытку."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не удалось распознать отпечаток. Повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Очистите сканер и повторите попытку."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Вы перемещали палец слишком медленно. Повторите попытку."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Не распознано"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечаток пальца проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Лицо распознано"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Приложение сможет изменять настройки синхронизации аккаунта. Например, с помощью этого разрешения можно включить в аккаунте синхронизацию контактов."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"Просмотр статистики синхронизации"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Приложение сможет просматривать статистику синхронизации аккаунта, в том числе историю событий и объем синхронизированных данных."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"Просмотр данных на USB-накопителе"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"Считывание содержимого SD-карты"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Приложение сможет считывать данные на USB-накопителе."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Приложение сможет считывать данные на SD-карте."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"Изменение/удаление данных на USB-накопителе"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"Изменение или удаление содержимого SD-карты"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Приложение сможет записывать данные на USB-накопитель."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Приложение сможет записывать данные на SD-карту."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"Просмотр данных на общем накопителе"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Приложение сможет считывать данные с общего накопителя."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"Изменение или удаление данных на общем накопителе"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Приложение сможет записывать данные на общий накопитель."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"Входящие и исходящие вызовы SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Разрешить вызовы по протоколу SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"регистрация новых SIM-карт"</string>
@@ -656,9 +652,9 @@
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Приложение сможет подключаться к сервисам оператора связи. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_access_notification_policy" msgid="4247510821662059671">"Доступ к режиму \"Не беспокоить\""</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Открывает приложению доступ к настройкам режима \"Не беспокоить\" и позволяет изменять их."</string>
- <string name="policylab_limitPassword" msgid="4497420728857585791">"Правила выбора паролей"</string>
+ <string name="policylab_limitPassword" msgid="4497420728857585791">"Настройка правил для паролей"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Контролировать длину и символы при вводе пароля и PIN-кода."</string>
- <string name="policylab_watchLogin" msgid="5091404125971980158">"Отслеживать попытки снять блокировку экрана"</string>
+ <string name="policylab_watchLogin" msgid="5091404125971980158">"Отслеживание попыток разблокировать экран"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует планшетный ПК или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Блокировка телевизора или удаление всех данных на нем при слишком большом количестве неудачных попыток ввести пароль для разблокировки экрана."</string>
<string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует телефон или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
@@ -679,11 +675,11 @@
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="6787904546711590238">"Удалить данные этого пользователя с телефона без предупреждения."</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Глобальный прокси-сервер"</string>
<string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Настроить глобальный прокси-сервер устройства, который будет использоваться при активной политике. Это может сделать только владелец устройства."</string>
- <string name="policylab_expirePassword" msgid="5610055012328825874">"Задать срок действия пароля"</string>
+ <string name="policylab_expirePassword" msgid="5610055012328825874">"Настройка срока действия пароля блокировки экрана"</string>
<string name="policydesc_expirePassword" msgid="5367525762204416046">"Установить частоту изменения пароля, PIN-кода или графического ключа."</string>
- <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Настроить шифрование хранилища"</string>
+ <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Настройка шифрования хранилища"</string>
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Шифровать данные приложений в хранилище."</string>
- <string name="policylab_disableCamera" msgid="6395301023152297826">"Отключить камеры"</string>
+ <string name="policylab_disableCamera" msgid="6395301023152297826">"Отключение камер"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Запретить использование камер на устройстве."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Отключение функций"</string>
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Запретить использовать некоторые функции блокировки экрана."</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Запланировать событие на выбранный день"</string>
<string name="view_flight" msgid="7691640491425680214">"Отследить"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Отслеживать выбранный рейс"</string>
+ <string name="translate" msgid="9218619809342576858">"Перевести"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Перевести выделенный текст"</string>
+ <string name="define" msgid="7394820043869954211">"Найти определение"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Найти определения слов в выбранном тексте"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Недостаточно памяти"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые функции могут не работать"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
@@ -1506,8 +1506,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> совпадений из <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Готово"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Очистка USB-накопителя..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Очистка SD-карты..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Очистка единого хранилища…"</string>
<string name="share" msgid="1778686618230011964">"Отправить"</string>
<string name="find" msgid="4808270900322985960">"Найти"</string>
<string name="websearch" msgid="4337157977400211589">"Веб-поиск"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-запрос преобразован в USSD-запрос"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Преобразовано в SS-запрос"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Рабочий профиль"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Отправлено оповещение"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Развернуть"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Скрыть"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"Свернуть или развернуть"</string>
@@ -2024,7 +2022,7 @@
<string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ОТКРЫТЬ"</string>
<string name="harmful_app_warning_title" msgid="8982527462829423432">"Обнаружено вредоносное приложение"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"Приложение \"<xliff:g id="APP_0">%1$s</xliff:g>\" запрашивает разрешение на показ фрагментов приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"."</string>
- <string name="screenshot_edit" msgid="7867478911006447565">"Изменить"</string>
+ <string name="screenshot_edit" msgid="7867478911006447565">"Редактировать"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"Для звонков и уведомлений включен вибросигнал."</string>
<string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"Для звонков и уведомлений отключен звук."</string>
<string name="notification_channel_system_changes" msgid="5072715579030948646">"Системные изменения"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index bd5b82a..7a43ecd 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi ඇමතුම් | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi ඇමතීම"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi ඇමතීම"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ක්රියාවිරහිතයි"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi වඩා කැමතියි"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ජංගම කැමතියි"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ඔබගේ මාධ්ය එකතුවෙන් ස්ථාන කියවීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> යෙදුම වෙත සත්යාපනය කිරීමට අවශ්යයි."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ජීවමිතික දෘඪාංග ලබා ගත නොහැකිය"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"හඳුනා නොගන්නා ලදී"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ඇඟිලි සලකුණ අඩ වශයෙන් අනාවරණය කර ගැනිණි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ඇඟිලි සලකුණු සංවේදකය අපිරිසිදුයි. කරුණාකර පිරිසිදු කර නැවත උත්සාහ කරන්න."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ඇඟිල්ල වඩා සෙමෙන් ගෙන යන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"හඳුනා නොගන්නා ලදී"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"මුහුණ සත්යාපනය කරන ලදී"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"මුහුණ සත්යාපනය කරන ලදී, කරුණාකර තහවුරු කරන්න ඔබන්න"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ගිණුමක් සඳහා සමමුහුර්ත සැකසීම් විකරණය කිරීමට යෙදුමකට ඉඩ දෙයි. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත කිරීම සබල කිරීමට මෙය භාවිත කළ හැක."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"සමමුහුර්ත කිරීමේ සංඛ්යාන කියවීම"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"සමමුහුර්ත කිරීමේ සිදුවීම් ඉතිහාසය සහ කෙතරම් දත්ත සමමුහුර්ත වී ඇතිදැයි ඇතුලත් ගිණුම සඳහා සමමුහුර්ත කිරීමේ සංඛ්යාන කියවීමට යෙදුමට අවසර දෙන්න."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ඔබගේ USB ආචයනය හි අන්තර්ගතය කියවන්න"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ඔබගේ SD කාඩ් පතෙහි අන්තර්ගතය කියවන්න"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"යෙදුමට ඔබගේ USB ආචයනය අන්තර්ගතය කියවීමට අවසර දෙන්න."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"යෙදුමට ඔබගේ SD කාඩ්පතේ අන්තර්ගතය කියවීමට ඉඩ දෙයි."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ඔබගේ USB ආචයනයේ අන්තර්ගත වෙනස් කිරීම හෝ මැකීම"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ඔබගේ SD පතේ අන්තර්ගත වෙනස් කිරීම හෝ මැකීම"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB ආචයනය වෙත ලිවීමට යෙදුමට අවසර දෙන්න."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SD පත වෙත ලිවීමට යෙදුමට අවසර දෙන්න."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"ඔබේ බෙදා ගත් ගබඩාවේ අන්තර්ගත කියවන්න"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"යෙදුමට ඔබේ බෙදා ගත් ගබඩාවේ අන්තර්ගත කියවීමට ඉඩ දෙයි."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ඔබේ බෙදා ගත් ගබඩාවේ අන්තර්ගත වෙනස් කරන්න නැතහොත් මකන්න"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"යෙදුමට ඔබේ බෙදා ගත් ගබඩාවේ අන්තර්ගත කියවීමට ඉඩ දෙයි."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP ඇමතුම් සිදුකිරීමට/ලබාගැනීමට"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP ඇමතුම් සිදුකිරීමට සහ ලබාගැනීමට යෙදුමට ඉඩ දෙන්න."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"අලුත් විදුලි සංදේශ SIM සම්බන්ධතාවක් ලියාපදිංචි කරන්න"</string>
@@ -1107,6 +1103,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"තෝරා ගත් වේලාව සඳහා සිදුවීම කාලසටහන්ගත කරන්න"</string>
<string name="view_flight" msgid="7691640491425680214">"ශ්රව්ය ඛණ්ඩය"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"තෝරා ගත් ගුවන් ගමන හඹා යන්න"</string>
+ <string name="translate" msgid="9218619809342576858">"පරිවර්තනය කරන්න"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"තෝරා ගත් පෙළ පරිවර්තනය කරන්න"</string>
+ <string name="define" msgid="7394820043869954211">"නිර්වචනය කරන්න"</string>
+ <string name="define_desc" msgid="7910883642444919726">"තෝරා ගත් පෙළ නිර්වචනය කරන්න"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ආචයනය ඉඩ ප්රමාණය අඩු වී ඇත"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"සමහර පද්ධති කාර්යයන් ක්රියා නොකරනු ඇත"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"පද්ධතිය සඳහා ප්රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
@@ -1462,8 +1462,7 @@
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g> න් <xliff:g id="INDEX">%d</xliff:g> යි</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"හරි"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB ආචයනය මකමින්..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD පත මකමින්..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"බෙදා ගත් ගබඩාව මකා දමමින්…"</string>
<string name="share" msgid="1778686618230011964">"බෙදාගන්න"</string>
<string name="find" msgid="4808270900322985960">"සොයන්න"</string>
<string name="websearch" msgid="4337157977400211589">"වෙබ් සෙවුම"</string>
@@ -1835,8 +1834,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS ඉල්ලීම USSD ඉල්ලීමට වෙනස් කරන ලදී"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"නව SS ඉල්ලීමට වෙනස් කරන ලදී"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"කාර්යාල පැතිකඩ"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"අනතුරු අඟවන ලදී"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"දිග හරින්න"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"හකුළන්න"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"පුළුල් කිරීම ටොගල කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ea45c9d..7e158c2 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Volanie cez WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Volanie cez Wi‑Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Volanie cez Wi‑Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Vypnuté"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferovať Wi‑Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferovať mobilné spojenie"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Umožňuje aplikácii čítať polohy zo zbierky médií."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikácia <xliff:g id="APP">%s</xliff:g> chce overiť totožnosť"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrický hardvér nie je k dispozícii"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nerozpoznané"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Podarilo sa rozpoznať iba časť odtlačku prsta. Skúste to znova."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Snímač odtlačkov je špinavý. Vyčistite ho a skúste to znova."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Pohli ste prstom príliš pomaly. Skúste to znova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nerozpoznané"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Odtlačok bol overený"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Tvár bola overená"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Tvár bola overená, stlačte tlačidlo potvrdenia"</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Umožňuje aplikácii upraviť nastavenia synchronizácie v účte. Pomocou tohto povolenia je možné napríklad povoliť synchronizáciu aplikácie Ľudia s účtom."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"čítať štatistické údaje o synchronizácii"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Umožňuje aplikácii čítať štatistické informácie o synchronizácii v účte vrátane histórie uskutočnených synchronizácií a informácií o množstve synchronizovaných údajov."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"čítať obsah úložiska USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"čítať obsah SD karty"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Povoľuje ap. čítať obsah USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Povoľuje aplikácii čítať obsah SD karty."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"upraviť alebo odstrániť obsah v úložisku USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"úprava alebo odstránenie obsahu na SD karte"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Umožňuje aplikácii zapisovať do úložiska USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikácii zápis na SD kartu."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"čítanie obsahu zdieľaného úložiska"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Umožňuje aplikácii čítať obsah zdieľaného úložiska."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"upravovanie alebo odstraňovanie obsahu zdieľaného úložiska"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Umožňuje aplikácii zapisovať obsah zdieľaného úložiska."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"uskutočňovanie/príjem hovorov SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Umožňuje aplikácii uskutočňovať a prijímať hovory SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrácia nových pripojení telekomunikačnej siete SIM"</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Naplánovať udalosť na vybratý čas"</string>
<string name="view_flight" msgid="7691640491425680214">"Sledovať"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Sledovať vybratý let"</string>
+ <string name="translate" msgid="9218619809342576858">"Preložiť"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Preložiť vybraný text"</string>
+ <string name="define" msgid="7394820043869954211">"Definovať"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definovať vybraný text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string>
@@ -1506,8 +1506,7 @@
<item quantity="one">1 zápas</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Hotovo"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Prebieha vymazávanie ukladacieho priestoru USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Prebieha vymazávanie SD karty..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Vymazáva sa zdieľané úložisko…"</string>
<string name="share" msgid="1778686618230011964">"Zdieľať"</string>
<string name="find" msgid="4808270900322985960">"Nájsť"</string>
<string name="websearch" msgid="4337157977400211589">"Hľadať na webe"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Žiadosť SS bola zmenená na žiadosť USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Zmenené na novú žiadosť SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Pracovný profil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Upozornené"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Rozbaliť"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Zbaliť"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"prepnúť rozbalenie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4a41228..0790e90 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi operaterja <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Klicanje prek WiFi-ja | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"Govor prek Wi-Fi-ja operaterja <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Klicanje prek Wi-Fi-ja"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Klicanje prek Wi-Fi-ja"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"Govor prek Wi-Fi-ja"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Izklopljeno"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Prednostno Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Prednostno mobilno"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Aplikaciji omogoča branje lokacij v predstavnostni zbirki."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi preveriti pristnost."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Strojna oprema za biometrične podatke ni na voljo"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ni prepoznano"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Zaznan delni prstni odtis. Poskusite znova."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Prstnega odtisa ni bilo mogoče obdelati. Poskusite znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Tipalo prstnih odtisov je umazano. Očistite ga in poskusite znova."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Prepočasen premik prsta. Poskusite znova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Ni prepoznano"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Pristnost prstnega odtisa je preverjena"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Pristnost obraza je potrjena"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Pristnost obraza je preverjena. Pritisnite gumb »Potrdi«."</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Aplikaciji omogoča spreminjanje nastavitev sinhronizacije za račun. S tem se lahko na primer omogoči sinhronizacijo aplikacije Ljudje z računom."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"branje statističnih podatkov sinhronizacije"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Aplikaciji omogoča branje statističnih podatkov o sinhronizaciji za račun, vključno z zgodovino dogodkov sinhronizacije in količino sinhroniziranih podatkov."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"branje vsebine pomnilnika USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"branje vsebine kartice SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Aplikaciji omogoča branje vsebine shrambe USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Aplikaciji omogoča branje vsebine kartice SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"spreminjanje ali brisanje vsebine shrambe USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"spreminjanje ali brisanje vsebine kartice SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Aplikaciji omogoča zapisovanje v pomnilnik USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Aplikaciji omogoča pisanje na kartico SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"branje vsebine skupne shrambe"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Aplikaciji omogoča branje vsebine skupne shrambe."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"spreminjanje ali brisanje vsebine skupne shrambe"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Aplikaciji omogoča zapisovanje vsebine skupne shrambe."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"opravljanje/sprejemanje klicev SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Aplikaciji omogoča opravljanje in sprejemanje klicev SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registriranje novih telekomunikacijskih povezav s kartico SIM"</string>
@@ -1127,24 +1123,28 @@
<string name="deleteText" msgid="6979668428458199034">"Izbriši"</string>
<string name="inputMethod" msgid="1653630062304567879">"Način vnosa"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Besedilna dejanja"</string>
- <string name="email" msgid="4560673117055050403">"E-pošta"</string>
+ <string name="email" msgid="4560673117055050403">"Pošlji e-pošto"</string>
<string name="email_desc" msgid="3638665569546416795">"Pošlji na izbrani naslov"</string>
<string name="dial" msgid="1253998302767701559">"Pokliči"</string>
<string name="dial_desc" msgid="6573723404985517250">"Pokliči izbrano telefonsko številko"</string>
- <string name="map" msgid="5441053548030107189">"Zemljevid"</string>
+ <string name="map" msgid="5441053548030107189">"Odpri zemljevid"</string>
<string name="map_desc" msgid="1836995341943772348">"Poišči izbrani naslov na zemljevidu"</string>
<string name="browse" msgid="1245903488306147205">"Odpri"</string>
<string name="browse_desc" msgid="8220976549618935044">"Odpri izbrani URL"</string>
- <string name="sms" msgid="4560537514610063430">"Sporočilo"</string>
+ <string name="sms" msgid="4560537514610063430">"Pošlji SMS"</string>
<string name="sms_desc" msgid="7526588350969638809">"Pošlji sporočilo na izbrano telefonsko številko"</string>
<string name="add_contact" msgid="7867066569670597203">"Dodaj"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Dodaj med stike"</string>
- <string name="view_calendar" msgid="979609872939597838">"Ogled"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Ogled izbranega časa v koledarju"</string>
+ <string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaži izbrani čas v koledarju"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Dodaj na razpored"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Razporedi dogodek na izbrano uro"</string>
- <string name="view_flight" msgid="7691640491425680214">"Sledenje"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Sledi"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Sledi izbranemu letu"</string>
+ <string name="translate" msgid="9218619809342576858">"Prevedi"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevedi izbrano besedilo"</string>
+ <string name="define" msgid="7394820043869954211">"Opredeli"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Opredeli izbrano besedilo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nekatere sistemske funkcije morda ne delujejo"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string>
@@ -1506,8 +1506,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> od <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Končano"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Brisanje pomnilnika USB ..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Brisanje kartice SD ..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Brisanje skupne shrambe …"</string>
<string name="share" msgid="1778686618230011964">"Deli z dr."</string>
<string name="find" msgid="4808270900322985960">"Najdi"</string>
<string name="websearch" msgid="4337157977400211589">"Spletno iskanje"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Zahteva SS je spremenjena v zahtevo USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Spremenjeno v novo zahtevo SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Delovni profil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Opozorilo prikazano"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Razširi"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Strni"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"preklop razširitve"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index fc88556..5c001ed 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi në <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Telefonatë me Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi në <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Telefonatë me Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Telefonatë me WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Çaktivizuar"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferohet Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferohet rrjeti celular"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Lejon aplikacionin të lexojë vendndodhjet nga koleksioni yt i medias."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacioni <xliff:g id="APP">%s</xliff:g> dëshiron të vërtetojë."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Nuk ofrohet harduer biometrik"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nuk njihet"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"U zbulua një gjurmë gishti e pjesshme. Provo përsëri."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Gjurma e gishtit nuk mund të përpunohej. Provo përsëri."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensori i gjurmës së gishtit nuk është i pastër. Pastroje dhe provo përsëri."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Gishti lëvizi shumë ngadalë. Provo përsëri."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Nuk njihet"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Gjurma e gishtit u vërtetua"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Fytyra u vërtetua"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Fytyra u vërtetua, shtyp \"Konfirmo\""</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Lejon një aplikacion të modifikojë cilësimet e sinkronizimit për një llogari. Për shembull, kjo mund të përdoret për të mundësuar sinkronizimin e aplikacionit \"Kontaktet\" me një llogari."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lexo statistikat e sinkronizimit"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Lejon një aplikacion të lexojë statistikat e sinkronizimit për një llogari, përfshirë historikun e ngjarjeve të sinkronizimit dhe se sa të dhëna janë sinkronizuar."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"lexo përmbajtjet e USB-së"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"lexo përmbajtjet e kartës tënde SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Lejon aplikacionin të lexojë përmbajtjet e USB-së tënde."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Lejon aplikacionin të lexojë përmbajtjet e kartës SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifiko ose fshi përmbajtjet e USB-së"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifiko ose fshi përmbajtjet e kartës SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Lejon aplikacionin të shkruajë në hapësirën ruajtëse të USB-së."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lejon aplikacionin të shkruajë në kartën SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"lexo përmbajtjet e hapësirës ruajtëse të ndarë"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Lejon aplikacionin të lexojë përmbajtjet e hapësirës ruajtëse të ndarë."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"modifiko ose fshi përmbajtjet e hapësirës ruajtëse të ndarë"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Lejon që aplikacioni të shkruajë përmbajtjet e hapësirës ruajtëse të ndarë."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"bëj/merr telefonata SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Lejon aplikacionin të kryejë dhe të marrë telefonata SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"regjistro lidhje të reja telekomunikimi të kartës SIM"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifiko ngjarjen për kohën e zgjedhur"</string>
<string name="view_flight" msgid="7691640491425680214">"Pjesë muzikore"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Monitoro fluturimin e zgjedhur"</string>
+ <string name="translate" msgid="9218619809342576858">"Përkthe"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Përkthe tekstin e zgjedhur"</string>
+ <string name="define" msgid="7394820043869954211">"Përkufizo"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Përkufizo tekstin e zgjedhur"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Hapësira ruajtëse po mbaron"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Disa funksione të sistemit mund të mos punojnë"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">1 përputhje</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"U krye!"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Po fshin USB-në..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Po fshin kartën SD…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Po fshin hapësirën ruajtëse të brendshme…"</string>
<string name="share" msgid="1778686618230011964">"Shpërndaj"</string>
<string name="find" msgid="4808270900322985960">"Gjej"</string>
<string name="websearch" msgid="4337157977400211589">"Kërkim në internet"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Kërkesa SS u ndryshua në kërkesë USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"U ndryshua në kërkesë të re SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profili i punës"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Sinjalizuar"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Zgjero"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Palos"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"aktivizo zgjerimin"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index b2e3059..b64f6fe 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -137,14 +137,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Позивање преко Wi-Fi-ја | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Позивање преко Wi-Fi-ја"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Позивање преко Wi-Fi-ја"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Искључено"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Предност има Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Желим мобилне податке"</string>
@@ -536,6 +532,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Дозвољава апликацији да чита локације из медијске колекције."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Апликација <xliff:g id="APP">%s</xliff:g> жели да потврди ваш идентитет."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометријски хардвер није доступан"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Није препознато"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откривен је делимични отисак прста. Пробајте поново."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Није успела обрада отиска прста. Пробајте поново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензор за отиске прстију је прљав. Очистите га и покушајте поново."</string>
@@ -543,7 +544,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Превише споро сте померили прст. Пробајте поново."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Није препознато"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отисак прста је потврђен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Лице је потврђено"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Лице је потврђено. Притисните Потврди"</string>
@@ -599,14 +599,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Дозвољава апликацији да мења подешавања синхронизације за налог. На пример, овако може да се омогући синхронизација апликације Људи са налогом."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"читање статистике о синхронизацији"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Дозвољава апликацији да чита статистику синхронизације за налог, укључујући историју синхронизованих догађаја и количину података који се синхронизују."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"читање садржаја USB меморије"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"читање садржаја SD картице"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Омогућава апликацији да чита садржај USB меморије."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Омогућава апликацији да чита садржај SD картице."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"измена или брисање садржаја USB меморије"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"мењање или брисање садржаја SD картице"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Дозвољава апликацији да уписује податке на USB меморију."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозвољава апликацији да уписује податке на SD картицу."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"читање садржаја дељеног меморијског простора"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Дозвољава апликацији да чита садржај дељеног меморијског простора."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"мењање или брисање садржаја дељеног меморијског простора"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Дозвољава апликацији да уписује садржај дељеног меморијског простора."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"упућивање/пријем SIP позива"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Омогућава апликацији да упућује и прима SIP позиве."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"региструје нове везе са телекомуникационим мрежама преко SIM картице"</string>
@@ -1125,6 +1121,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Закажите догађај у изабрано време"</string>
<string name="view_flight" msgid="7691640491425680214">"Прати"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Пратите изабрани лет"</string>
+ <string name="translate" msgid="9218619809342576858">"Преведи"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Преведите изабрани текст"</string>
+ <string name="define" msgid="7394820043869954211">"Дефиниши"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Дефинишите изабрани текст"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Меморијски простор је на измаку"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
@@ -1483,8 +1483,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> од <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Готово"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Брисање USB меморије..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Брисање SD картице..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Брише се дељени меморијски простор…"</string>
<string name="share" msgid="1778686618230011964">"Дели"</string>
<string name="find" msgid="4808270900322985960">"Пронађи"</string>
<string name="websearch" msgid="4337157977400211589">"Веб-претрага"</string>
@@ -1866,8 +1865,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS захтев је промењен у USSD захтев"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Промењено је у нови SS захтев"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Профил за Work"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Обавештено"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Прошири"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Скупи"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"укључите/искључите проширење"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index dbe9b69..18c66ed 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi via <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi-samtal | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi via <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi-samtal"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi-samtal"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Av"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi i första hand"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Använd mobildata"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tillåter att appen läser av platser i din mediesamling."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Appen <xliff:g id="APP">%s</xliff:g> vill autentisera"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk maskinvara är inte tillgänglig"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Identifierades inte"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Ofullständigt fingeravtryck. Försök igen."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtryckssensorn är smutsig. Rengör den och försök igen."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Du rörde fingret för långsamt. Försök igen."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Identifierades inte"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeravtrycket har autentiserats"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Ansiktet har autentiserats"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Ansiktet har autentiserats. Tryck på Bekräfta"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Tillåter att appen ändrar synkroniseringsinställningarna för ett konto. Detta kan användas till exempel för att synkronisera appen Personer med ett konto."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"läsa synkroniseringsstatistik"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Tillåter att appen läser synkroniseringsstatistik för ett konto, inklusive historiken för synkroniseringshändelser och mängden data som synkroniseras."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"läsa innehåll på USB-enheten"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"läsa innehållet på SD-kortet"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Låter appen läsa USB-innehåll."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Tillåter att appen läser innehållet på SD-kortet."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ändra eller ta bort innehåll på USB-enheten"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ändra eller ta bort innehåll på SD-kortet"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Gör att app skriver till USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillåter att appen skriver till SD-kortet."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"läsa innehåll på delat lagringsutrymmet"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Tillåter att appen läser innehåll på ditt delade lagringsutrymme."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"ändra eller ta bort innehåll på delat lagringsutrymme"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Tillåter att appen skriver innehåll på ditt delade lagringsutrymme."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"gör/ta emot SIP-anrop"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Tillåter att appen gör och tar emot SIP-anrop."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"registrera nya telekommunikationsanslutningar för SIM-kortet"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Schemalägg händelse på den valda tiden"</string>
<string name="view_flight" msgid="7691640491425680214">"Spår"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Spåra valt flyg"</string>
+ <string name="translate" msgid="9218619809342576858">"Översätt"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Översätt markerad text"</string>
+ <string name="define" msgid="7394820043869954211">"Definiera"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definiera markerad text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string>
@@ -1332,7 +1332,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Ett tillbehör med analog ljudutgång hittades"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Den anslutna enheten är inte kompatibel med mobilen. Tryck här om du vill veta mer."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-felsökning ansluten"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"Tryck om du vill inaktivera USB-felsökning"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"Tryck för att inaktivera USB-felsökning"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Välj för att inaktivera USB-felsökning."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Felrapporten överförs …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vill du dela felrapporten?"</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 träff</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Klar"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Raderar USB-lagring ..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Raderar SD-kort ..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Delat lagringsutrymme rensas …"</string>
<string name="share" msgid="1778686618230011964">"Dela"</string>
<string name="find" msgid="4808270900322985960">"Sök efter"</string>
<string name="websearch" msgid="4337157977400211589">"Webbsökning"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-begäran har ändrats till en USSD-begäran"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Har ändrats till ny SS-begäran"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Jobbprofil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Aviserad"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Utöka"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Komprimera"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"Utöka/komprimera"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 46434cb..51ac513 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi ya <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Kupiga Simu kupitia WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi ya <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Kupiga simu kupitia Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Kupiga simu kupitia WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Imezimwa"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi inapedelewa"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mtandao wa simu unapendelewa"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Inaruhusu programu kusoma maeneo kwenye mkusanyiko wa vipengee vyako."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Programu ya <xliff:g id="APP">%s</xliff:g> inataka kuthibitishwa."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Maunzi ya bayometriki hayapatikani"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Hayatambuliki"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Kitambuzi kimegundua sehemu ya kitambulisho. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Haikuweza kuchakata kitambulisho. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kitambuzi alama ya kidole ni kichafu. Tafadhali kisafishe na ujaribu tena."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Kidole kilisogezwa polepole zaidi. Tafadhali jaribu tena."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Hayatambuliki"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Imethibitisha alama ya kidole"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Uso umethibitishwa"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Inaruhusu programu kurekebisha mipangalio ya upatanishi wa akaunti. Kwa mfano, hii inaweza kuwezesha programu ya upatanishi wa Watu na akaunti."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"kusoma takwimu za usawazishaji"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Inaruhusu programu kusoma takwimu za upatanishi za akaunti, ikiwa ni pamoja na historia ya matukio ya upatanishi na kiasi cha data kimepatanishwa."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"kusoma maudhui yaliyo kwenye hifadhi yako ya USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"soma maudhui ya kadi yako ya SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Huruhusu programu kusoma maudhui ya hifadhi ya USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Huruhusu programu kusoma maudhui ya kadi yako ya SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"kurekebisha au kufuta maudhui ya hifadhi yako ya USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"kurekebisha au kufuta maudhui ya kadi yako ya SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Inaruhusu programu kuandikia hifadhi ya USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Inaruhusu programu kuandikia kadi ya SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"isome maudhui ya hifadhi unayoshiriki"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Huruhusu programu isome maudhui ya hifadhi unayoshiriki."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"irekebishe au ifute maudhui ya hifadhi unayoshiriki"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Huruhusu programu iandike maudhui ya hifadhi unayoshiriki."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"piga/pokea simu za SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Ruhusu programu ipige na kupokea simu za SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"andikisha miunganisho mipya ya SIM ya mawasiliano ya simu"</string>
@@ -1100,11 +1096,15 @@
<string name="add_contact" msgid="7867066569670597203">"Ongeza"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Ongeza kwenye anwani"</string>
<string name="view_calendar" msgid="979609872939597838">"Angalia"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Angalia wakati uliyochagua katika kalenda"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Ratiba"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Angalia wakati uliochagua katika kalenda"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Ratibu"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ratibu tukio la wakati uliochagua"</string>
- <string name="view_flight" msgid="7691640491425680214">"Toleo"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Fuatilia"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Fuatilia ndege uliyochagua"</string>
+ <string name="translate" msgid="9218619809342576858">"Tafsiri"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Tafsiri maandishi yaliyochaguliwa"</string>
+ <string name="define" msgid="7394820043869954211">"Fafanua"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Fafanua maandishi yaliyochaguliwa"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 inayolingana</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Nimemaliza"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Inafuta hifadhi ya USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Inafuta kadi ya SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Inafuta hifadhi iliyoshirikiwa…"</string>
<string name="share" msgid="1778686618230011964">"Shiriki"</string>
<string name="find" msgid="4808270900322985960">"Tafuta"</string>
<string name="websearch" msgid="4337157977400211589">"Utafutaji Wavuti"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Imebadilisha ombi la SS kuwa ombi la USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Imebadilisha kuwa ombi jipya la SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Wasifu wa kazini"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Imearifu"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Panua"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Kunja"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"geuza upanuzi"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index d5b8cf8..1f2c6a6 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> வைஃபை"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"வைஃபை அழைப்பு | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"வைஃபை அழைப்பு"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"வைஃபை"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"வைஃபை அழைப்பு"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ஆஃப்"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"வைஃபைக்கு முன்னுரிமை"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"மொபைல் தரவிற்கு முன்னுரிமை"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"உங்களின் மீடியா தொகுப்பிலிருந்து இடங்களை அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கும்."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g>ஐப் பயன்படுத்த அங்கீகாரத்தை உறுதிசெய்க."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"பயோமெட்ரிக் வன்பொருள் இல்லை"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"அடையாளங்காணபடவில்லை"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"கைரேகையை ஓரளவுதான் கண்டறிய முடிந்தது. மீண்டும் முயலவும்."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"கைரேகை உணர்வியில் தூசி உள்ளது. சுத்தம் செய்து, முயலவும்."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"விரலை மிகவும் மெதுவாக நகர்த்திவிட்டீர்கள். மீண்டும் முயற்சிக்கவும்."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"அடையாளங்காணபடவில்லை"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"கைரேகை அங்கீகரிக்கப்பட்டது"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"முகம் அங்கீகரிக்கப்பட்டது"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"முகம் அங்கீகரிக்கப்பட்டது. ’உறுதிப்படுத்துக’ என்பதை அழுத்துக"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"கணக்கிற்கான ஒத்திசைவு அமைப்புகளைத் திருத்த பயன்பாட்டை அனுமதிக்கிறது. எடுத்துக்காட்டாக, பீப்பிள் பயன்பாட்டைக் கணக்குடன் ஒத்திசைவை இயக்குவதற்கு இது பயன்படுத்தப்படலாம்."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ஒத்திசைவு புள்ளிவிவரங்களைப் படித்தல்"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"நிகழ்வுகள் ஒத்திசைவின் வரலாறு மற்றும் ஒத்திசைக்கப்பட்ட தரவு எவ்வளவு ஆகியன உட்பட, கணக்கிற்கான ஒத்திசைவு புள்ளிவிவரங்களைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"உங்கள் USB சேமிப்பிடத்தின் உள்ளடக்கங்களைப் படித்தல்"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"உங்கள் SD கார்டின் உள்ளடக்கங்களைப் படித்தல்"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"உங்கள் USB சேமிப்பிடத்தின் உள்ளடக்கங்களைப் படிக்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"உங்கள் SD கார்டின் உள்ளடக்கங்களைப் படிக்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"உங்கள் USB சேமிப்பிடத்தின் உள்ளடக்கங்களை மாற்றுதல் அல்லது நீக்குதல்"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"உங்கள் SD கார்டின் உள்ளடக்கங்களை மாற்றுதல் அல்லது நீக்குதல்"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB சேமிப்பகத்தில் எழுத, பயன்பாட்டை அனுமதிக்கிறது."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SD கார்டில் எழுத, பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"பகிர்ந்த சேமிப்பகத்தின் உள்ளடக்கங்களைப் பார்த்தல்"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"பகிர்ந்த சேமிப்பகத்தின் உள்ளடக்கங்களைப் பார்க்க ஆப்ஸை அனுமதிக்கும்."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"பகிர்ந்த சேமிப்பகத்தின் உள்ளடக்கங்களை மாற்றும் அல்லது நீக்கும்"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"பகிர்ந்த சேமிப்பகத்தின் உள்ளடக்கத்தில் மாற்றங்களைச் செய்ய அனுமதிக்கும்."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP அழைப்புகளைச் செய்தல்/பெறுதல்"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP அழைப்புகளைச் செய்ய/பெற, பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"புதிய நிறுவன சிம் இணைப்புகளைப் பதிவுசெய்தல்"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"தேர்ந்தெடுத்த நேரத்திற்கு நிகழ்வைத் திட்டமிடும்"</string>
<string name="view_flight" msgid="7691640491425680214">"கண்கானி"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"தேர்ந்தெடுத்த விமானத்தைக் கண்காணிக்கும்"</string>
+ <string name="translate" msgid="9218619809342576858">"மொழிபெயர்"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"தேர்ந்தெடுத்த உரையை மொழிபெயர்க்கும்"</string>
+ <string name="define" msgid="7394820043869954211">"விளக்குக"</string>
+ <string name="define_desc" msgid="7910883642444919726">"தேர்ந்தெடுத்த உரையை விளக்கும்"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"சேமிப்பிடம் குறைகிறது"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">1 பொருத்தம்</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"முடிந்தது"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB சேமிப்பிடத்தை அழிக்கிறது…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD கார்டை அழிக்கிறது…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"பகிர்ந்த சேமிப்பகத்தை அழிக்கிறது…"</string>
<string name="share" msgid="1778686618230011964">"பகிர்"</string>
<string name="find" msgid="4808270900322985960">"கண்டறி"</string>
<string name="websearch" msgid="4337157977400211589">"இணையத் தேடல்"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS கோரிக்கை, USSD கோரிக்கைக்கு மாற்றப்பட்டது"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"புதிய SS கோரிக்கைக்கு மாற்றப்பட்டது"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"பணி சுயவிவரம்"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"விழிப்பூட்டல் ஐகான்"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"விரிவாக்கும்"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"சுருக்கும்"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"விரிவாக்கத்தை நிலைமாற்றும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 37d4a7b..fd853fb 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi కాలింగ్ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi కాలింగ్"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi కాలింగ్"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ఆఫ్లో ఉంది"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fiకి ప్రాధాన్యత"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"మొబైల్కి ప్రాధాన్యత ఇవ్వబడింది"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"అప్లికేషన్ <xliff:g id="APP">%s</xliff:g>కు ప్రమాణీకరణ అవసరం."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"బయోమెట్రిక్ హార్డ్వేర్ అందుబాటులో లేదు"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"గుర్తించలేదు"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"పాక్షిక వేలిముద్ర గుర్తించబడింది. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"వేలిముద్ర సెన్సార్ మురికిగా ఉంది. దయచేసి శుభ్రపరిచి, మళ్లీ ప్రయత్నించండి."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"వేలిని చాలా నెమ్మదిగా కదిలించారు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"గుర్తించలేదు"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"వేలిముద్ర ప్రమాణీకరించబడింది"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ముఖం ప్రమాణీకరించబడింది"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ముఖం ప్రమాణీకరించబడింది, దయచేసి ధృవీకరించును నొక్కండి"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్లను సవరించడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సమకాలీకరణను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"సమకాలీకరణ గణాంకాలను చదవడం"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ఖాతా యొక్క సమకాలీకరణ గణాంకాలను అలాగే సమకాలీకరణ ఈవెంట్ల చరిత్రను మరియు ఎంత డేటా సమకాలీకరించబడింది అనేవాటిని చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"మీ USB నిల్వ యొక్క కంటెంట్లను చదవడం"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"మీ SD కార్డు యొక్క కంటెంట్లను చదవడం"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"మీ USB నిల్వలోని కంటెంట్లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"మీ SD కార్డ్లోని కంటెంట్లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"మీ USB నిల్వ యొక్క కంటెంట్లను సవరించడం లేదా తొలగించడం"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"మీ SD కార్డు యొక్క కంటెంట్లను సవరించడం లేదా తొలగించడం"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB నిల్వకు వ్రాయడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SD కార్డుకి వ్రాయడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను చదువుతుంది"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను సవరించండి లేదా తొలగించండి"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను రాయడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP కాల్లను చేయడానికి/స్వీకరించడానికి"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"SIP కాల్లను చేయడానికి మరియు స్వీకరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"కొత్త టెలికామ్ SIM కనెక్షన్లను నమోదు చేయడం"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ఎంచుకున్న సమయానికి ఈవెంట్ను షెడ్యూల్ చేస్తుంది"</string>
<string name="view_flight" msgid="7691640491425680214">"ట్రాక్ చేయండి"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ఎంచుకున్న విమానాన్ని ట్రాక్ చేస్తుంది"</string>
+ <string name="translate" msgid="9218619809342576858">"అనువదించండి"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"ఎంచుకున్న వచనాన్ని అనువదించండి"</string>
+ <string name="define" msgid="7394820043869954211">"నిర్వచించండి"</string>
+ <string name="define_desc" msgid="7910883642444919726">"ఎంచుకున్న వచనాన్ని నిర్వచించండి"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"నిల్వ ఖాళీ అయిపోతోంది"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">1 సరిపోలిక</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"పూర్తయింది"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB నిల్వను ఎరేజ్ చేస్తోంది…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD కార్డును ఎరేజ్ చేస్తోంది…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"షేర్ చేసిన నిల్వను తొలగిస్తోంది…"</string>
<string name="share" msgid="1778686618230011964">"భాగస్వామ్యం చేయండి"</string>
<string name="find" msgid="4808270900322985960">"కనుగొనండి"</string>
<string name="websearch" msgid="4337157977400211589">"వెబ్ శోధన"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS అభ్యర్థన USSD అభ్యర్థనకు మార్చబడింది"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"కొత్త SS అభ్యర్థనకు మార్చబడింది"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"కార్యాలయ ప్రొఫైల్"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"హెచ్చరించబడింది"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"విస్తరింపజేయి"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"కుదించు"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"విస్తరణను టోగుల్ చేయండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 1b473f0..9ce2930 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"การโทรผ่าน Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"การโทรผ่าน Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"การโทรผ่าน Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"ปิด"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"ต้องการใช้ Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"ต้องการใช้อินเทอร์เน็ตมือถือ"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"อนุญาตให้แอปอ่านตำแหน่งจากคอลเล็กชันสื่อของคุณ"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"แอปพลิเคชัน <xliff:g id="APP">%s</xliff:g> ต้องการตรวจสอบสิทธิ์"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ฮาร์ดแวร์ไบโอเมตริกไม่พร้อมใช้งาน"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ไม่รู้จัก"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ตรวจพบลายนิ้วมือเพียงบางส่วน โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ไม่สามารถประมวลผลลายนิ้วมือได้ โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"เซ็นเซอร์ลายนิ้วมือไม่สะอาด โปรดทำความสะอาดและลองอีกครั้ง"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"นิ้วเคลื่อนที่ช้าเกินไป โปรดลองอีกครั้ง"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"ไม่รู้จัก"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ตรวจสอบสิทธิ์ใบหน้าแล้ว โปรดกดยืนยัน"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น สามารถใช้การอนุญาตเปิดใช้งานการซิงค์แอปพลิเคชัน People กับบัญชี"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"อ่านสถิติการซิงค์"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"อนุญาตให้แอปพลิเคชันอ่านสถานะการซิงค์ของบัญชี ซึ่งรวมถึงประวัติกิจกรรมการซิงค์และปริมาณข้อมูลที่ซิงค์"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"อ่านเนื้อหาในที่จัดเก็บ USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"อ่านเนื้อหาในการ์ด SD ของคุณ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"อนุญาตให้แอปอ่านเนื้อหาในที่จัดเก็บข้อมูล USB"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"อนุญาตให้แอปอ่านเนื้อหาในการ์ด SD"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"แก้ไขหรือลบเนื้อหาใน USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"แก้ไขหรือลบเนื้อหาในการ์ด SD ของคุณ"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"อนุญาตให้แอปเขียนลงใน USB"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"อนุญาตให้แอปพลิเคชันเขียนลงบนการ์ด SD"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"อ่านเนื้อหาในพื้นที่จัดเก็บข้อมูลที่ใช้ร่วมกัน"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"อนุญาตให้แอปอ่านเนื้อหาในพื้นที่จัดเก็บข้อมูลที่ใช้ร่วมกัน"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"แก้ไขหรือลบเนื้อหาในพื้นที่จัดเก็บข้อมูลที่ใช้ร่วมกัน"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"อนุญาตให้แอปเขียนเนื้อหาในพื้นที่จัดเก็บข้อมูลที่ใช้ร่วมกัน"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"โทร/รับสาย SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"อนุญาตให้แอปโทรและรับสาย SIP"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"ลงทะเบียนการเชื่อมต่อซิมโทรคมนาคมใหม่"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ตั้งเวลากิจกรรมสำหรับเวลาที่เลือก"</string>
<string name="view_flight" msgid="7691640491425680214">"แทร็ก"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ติดตามเที่ยวบินที่เลือก"</string>
+ <string name="translate" msgid="9218619809342576858">"แปล"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"แปลข้อความที่เลือก"</string>
+ <string name="define" msgid="7394820043869954211">"หาความหมาย"</string>
+ <string name="define_desc" msgid="7910883642444919726">"ให้คำจำกัดความข้อความที่เลือก"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">ตรงกัน 1 รายการ</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"เสร็จสิ้น"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"กำลังลบที่จัดเก็บข้อมูล USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"กำลังลบการ์ด SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"กำลังลบพื้นที่เก็บข้อมูลที่แชร์…"</string>
<string name="share" msgid="1778686618230011964">"แชร์"</string>
<string name="find" msgid="4808270900322985960">"ค้นหา"</string>
<string name="websearch" msgid="4337157977400211589">"ค้นเว็บ"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"คำขอ SS เปลี่ยนเป็นคำขอ USSD แล้ว"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"เปลี่ยนเป็นคำขอ SS ใหม่แล้ว"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"โปรไฟล์งาน"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"แจ้งเตือนแล้ว"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"ขยาย"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"ยุบ"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"สลับการขยาย"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 3e3b4f9..1193625 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi ng <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Pagtawag Gamit ang Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi ng <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Pagtawag Gamit ang Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Pagtawag Gamit ang WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Naka-off"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Mas gusto ang Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mas gusto ang mobile"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Pinapayagan ang app na basahin ang mga lokasyon mula sa iyong koleksyon ng media."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Gustong mag-authenticate ng app na <xliff:g id="APP">%s</xliff:g>."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Walang biometric hardware"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Hindi nakilala"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hindi buo ang natukoy na fingerprint. Pakisubukang muli."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Hindi maproseso ang fingerprint. Pakisubukang muli."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Marumi ang sensor ng fingerprint. Pakilinis at subukang muli."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Masyadong mabagal ang paggalaw ng daliri. Pakisubukang muli."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Hindi nakilala"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Na-authenticate ang fingerprint"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Na-authenticate ang mukha"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Na-authenticate ang mukha, pakipindot ang kumpirmahin"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Pinapayagan ang isang app na baguhin ang mga setting ng pag-sync para sa isang account. Halimbawa, magagamit ito upang paganahin ang pag-sync ng app na Mga Tao sa isang account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"basahin ang mga istatistika ng sync"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Pinapayagan ang app na basahin ang mga istatistika ng pag-sync para sa isang account, kabilang ang kasaysayan ng mga event sa pag-sync at kung ilang data ang naka-sync."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"basa nilalaman USB storage mo"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"basahin ang mga nilalaman ng iyong SD card"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Pinapayagan ang app na basahin ang mga nilalaman ng iyong USB storage."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Pinapayagan ang app na basahin ang mga nilalaman ng iyong SD card."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"Baguhin/I-delete ang laman ng USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"baguhin o i-delete ang mga nilalaman ng iyong SD card"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Pinapayagan ang app na magsulat sa USB storage."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pinapayagan ang app na magsulat sa SD card."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"i-read ang content ng nakabahagi mong storage"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Pinapayagan ang app na i-read ang content ng nakabahagi mong storage."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"baguhin o i-delete ang content ng nakabahagi mong storage"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Pinapayagan ang app na mag-write sa content ng nakabahagi mong storage."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"magsagawa/tumanggap ng mga tawag sa SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Pinapayagan ang app na magsagawa at makatanggap ng mga tawag sa SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"magrehistro ng mga bagong koneksyon sa SIM ng telecom"</string>
@@ -655,7 +651,7 @@
<string name="policylab_watchLogin" msgid="5091404125971980158">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Subaybayan ang bilang ng mga hindi tamang password na na-type kapag ina-unlock ang screen, at i-lock ang tablet o burahin ang lahat ng data ng tablet kung masyadong maraming hindi tamang password ang na-type."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Subaybayan ang bilang ng mga maling password kapag ina-unlock ang screen at i-lock ang TV o burahin ang lahat ng data ng TV kung masyadong maraming maling password ang nata-type."</string>
- <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Subaybayan ang bilang ng mga hindi tamang password na na-type. kapag ina-unlock ang screen, at i-lock ang telepono o burahin ang lahat ng data ng telepono kung masyadong maraming hindi tamang password ang na-type."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang telepono o burahin ang lahat ng data ng telepono kung masyadong maraming maling password ang na-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang tablet o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang TV o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang telepono o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang nata-type."</string>
@@ -677,7 +673,7 @@
<string name="policydesc_expirePassword" msgid="5367525762204416046">"Baguhin kung gaano kadalas dapat palitan ang password, PIN o pattern sa screen lock."</string>
<string name="policylab_encryptedStorage" msgid="8901326199909132915">"Itakda pag-encrypt ng storage"</string>
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Hilinging naka-encrypt ang nakaimbak na data ng app."</string>
- <string name="policylab_disableCamera" msgid="6395301023152297826">"Huwag paganahin mga camera"</string>
+ <string name="policylab_disableCamera" msgid="6395301023152297826">"I-disable ang mga camera"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Pigilan ang paggamit sa lahat ng camera ng device."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"I-disable ilang screen lock feature"</string>
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Pigilan ang paggamit ng ilang feature ng screen lock."</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Mag-iskedyul ng event para sa piniling oras"</string>
<string name="view_flight" msgid="7691640491425680214">"Subaybayan"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"I-track ang piniling flight"</string>
+ <string name="translate" msgid="9218619809342576858">"Isalin"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Isalin ang napiling text"</string>
+ <string name="define" msgid="7394820043869954211">"Ilarawan"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Ilarawan ang piniling text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string>
@@ -1331,7 +1331,7 @@
<string name="usb_power_notification_message" msgid="4647527153291917218">"China-charge ang nakakonektang device. Mag-tap para sa higit pang opsyon."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"May na-detect na analog na audio accessory"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Hindi tugma sa teleponong ito ang naka-attach na device. I-tap upang matuto pa."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"Konektado ang debugging ng USB"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Nakakonekta ang pag-debug ng USB"</string>
<string name="adb_active_notification_message" msgid="7463062450474107752">"I-tap para i-off ang pag-debug ng USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Piliin upang i-disable ang debugging ng USB."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Kinukuha ang ulat ng bug…"</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> ng <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Tapos na"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Binubura ang USB storage..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Binubura ang SD card..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Binubura ang nakabahaging storage…"</string>
<string name="share" msgid="1778686618230011964">"Ibahagi"</string>
<string name="find" msgid="4808270900322985960">"Hanapin"</string>
<string name="websearch" msgid="4337157977400211589">"Paghahanap sa Web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Ginawang USSD na kahilingan ang SS na kahilingan"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Ginawang bagong SS na kahilingan"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profile sa trabaho"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Naalertuhan"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Palawakin"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"I-collapse"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"i-toggle ang pagpapalawak"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e326b2c..521da3f 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Kablosuz"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Kablosuz Çağrı | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Kablosuz Çağrı"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Kablosuz"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Kablosuz Çağrı"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Kapalı"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Kablosuz bağlantı tercihli"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobil tercihli"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Uygulamanın medya koleksiyonunuzdaki konumları okumasına izin verir."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> uygulaması kimlik doğrulamak istiyor."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biyometrik donanım kullanılamıyor"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmadı"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Parmak izinin tümü algılanamadı. Lütfen tekrar deneyin."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Parmak izi sensörü kirli. Lütfen temizleyin ve tekrar deneyin."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Parmak hareketi çok yavaştı. Lütfen tekrar deneyin."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmadı"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Parmak izi kimlik doğrulaması yapıldı"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Yüz kimliği doğrulandı"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Uygulamaya bir hesaba ait senkronizasyon ayarlarını değiştirme izni verir. Örneğin, bu izne sahip bir uygulama Kişiler uygulamasının bir hesapla senkronize edilmesini etkinleştirebilir."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"senk. istatistiklerini okuma"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Uygulamaya bir hesaba ait senkronizasyon istatistiklerini okuma izni verir. Buna senkronizasyon etkinlikleri geçmişi ve senkronize edilen veri miktarı bilgileri de dahildir."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB belleğini okuma"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD kartımın içeriğini oku"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Uygulamaya, USB depolama biriminizin içeriğini okuma izni verir."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Uygulamaya, SD kartınızın içeriğini okuma izni verir."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB belleğinizin içeriğini değiştirme veya silme"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD kartın içeriğini değiştir veya sil"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Uygulamaya USB belleğe yazma izni verir."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Uygulamaya, SD karta yazma izni verir."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"paylaşılan depolama alanının içeriğini oku"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Uygulamaya, paylaşılan depolama alanınızın içeriğini okuma izni verir."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"paylaşılan depolama alanımın içeriğini değiştir veya sil"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Uygulamanın paylaşılan depolama alanınıza içerik yazmasına izin verir."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP çağrıları yapma/alma"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Uygulamanın SIP çağrıları yapmasına ve almasına izin verir."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"yeni telekomünikasyon SIM bağlantılarını kaydettir"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Seçilen zaman için etkinlik programla"</string>
<string name="view_flight" msgid="7691640491425680214">"İzle"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seçilen uçuşu takip et"</string>
+ <string name="translate" msgid="9218619809342576858">"Çevir"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Seçili metni çevir"</string>
+ <string name="define" msgid="7394820043869954211">"Tanımla"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Seçilen metni tanımla"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 eşleşme</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Bitti"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB bellek siliniyor…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD kart siliniyor…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Paylaşılan depolama alanı siliniyor…"</string>
<string name="share" msgid="1778686618230011964">"Paylaş"</string>
<string name="find" msgid="4808270900322985960">"Bul"</string>
<string name="websearch" msgid="4337157977400211589">"Google Web Arama"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS isteği USSD isteği olarak değişti"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Yeni SS isteği olarak değişti"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"İş profili"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Sesli uyarıldı"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Genişlet"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Daralt"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"genişletmeyi aç/kapat"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f9cbfd6..2bac0c3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -138,14 +138,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> через Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Виклики через Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> через VoWi-Fi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Дзвінки через Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Дзвінки через Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"Передавання голосу через Wi-Fi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Вимкнено"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi за умовчанням"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Мобільна мережа за умовчанням"</string>
@@ -539,6 +535,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Додаток зможе розпізнавати геодані з вашої колекції медіа-вмісту."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Для додатка <xliff:g id="APP">%s</xliff:g> потрібна автентифікація."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Біометричне апаратне забезпечення недоступне"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не розпізнано"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Відбиток розпізнано частково. Повторіть спробу."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не вдалось обробити відбиток. Повторіть спробу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчик відбитків забруднився. Очистьте його та повторіть спробу."</string>
@@ -546,7 +547,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Ви провели пальцем надто повільно. Повторіть спробу."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Не розпізнано"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Відбиток автентифіковано"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Обличчя автентифіковано"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string>
@@ -602,14 +602,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Дозволяє програмі змінювати налаштування синхронізації для облікового запису, наприклад, вмикати синхронізацію програми Люди з обліковим записом."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"Перегляд статистики синхронізації"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Дозволяє програмі читати статистику синхронізації облікового запису, зокрема історію синхронізацій і обсяг синхронізованих даних."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"читати вміст носія USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"читати вміст карти SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Дозволяє програмі читати вміст носія USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Дозволяє програмі читати вміст карти SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"змінювати чи видаляти дані на USB-носії"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"змінювати чи видаляти вміст на карті SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Додаток зможе записувати дані на USB-носій"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозволяє програмі записувати на карту SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"переглядати вміст у спільній пам’яті"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Додаток може переглядати вміст у спільній пам’яті."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"змінювати чи видаляти вміст у спільній пам’яті"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Додаток може писати вміст у спільній пам’яті."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"здійснювати й отримувати дзвінки через протокол SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Додаток зможе здійснювати й отримувати дзвінки через протокол SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"реєструвати нові телекомунікаційні з’єднання SIM-карт"</string>
@@ -1145,6 +1141,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Запланувати подію на вибраний час"</string>
<string name="view_flight" msgid="7691640491425680214">"Відстежити"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Відстежувати вибраний авіарейс"</string>
+ <string name="translate" msgid="9218619809342576858">"Перекласти"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Перекласти виділений текст"</string>
+ <string name="define" msgid="7394820043869954211">"Знайти визначення"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Знайти визначення виділеного тексту"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
@@ -1506,8 +1506,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> із <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Готово"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Стирання носія USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Стирання карти SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Стирання спільної пам’яті…"</string>
<string name="share" msgid="1778686618230011964">"Надіслати"</string>
<string name="find" msgid="4808270900322985960">"Знайти"</string>
<string name="websearch" msgid="4337157977400211589">"Веб-пошук"</string>
@@ -1899,8 +1898,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Запит SS змінено на запит USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Змінено на новий запит SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Робочий профіль"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Зі звуком"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Розгорнути"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Згорнути"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"розгорнути або згорнути"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 60b650d..2266221 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi کالنگ | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi کالنگ"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi کالنگ"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"آف"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi ترجیحی"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"موبائل ترجیحی"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ایپ کو آپ کی میڈيا کے مجموعے سے مقامات پڑھنے کی اجازت دیتا ہے۔"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"ایپلیکیشن <xliff:g id="APP">%s</xliff:g> تصدیق کرنا چاہتی ہے۔"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"تسلیم شدہ نہیں ہے"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"جزوی فنگر پرنٹ کی شناخت ہوئی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"فنگر پرنٹ پر کارروائی نہیں کی جا سکی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"فنگر پرنٹ سینسر گندا ہے۔ براہ کرم صاف کریں اور دوبارہ کوشش کریں۔"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"انگلی کو بہت آہستہ ہٹایا گیا۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"تسلیم شدہ نہیں ہے"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"فنگر پرنٹ کی تصدیق ہو گئی"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"چہرے کی تصدیق ہو گئی"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"چہرے کی تصدیق ہو گئی، براہ کرم \'تصدیق کریں\' کو دبائيں"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ایپ کو کسی اکاؤنٹ کیلئے مطابقت پذیری کی ترتیبات میں ترمیم کرنے کی اجازت دیتا ہے۔ مثلا، کسی اکاؤنٹ کے ساتھ People ایپ کی مطابقت پذیری فعال کرنے کیلئے اسے استعمال کیا جاسکتا ہے۔"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"مطابقت پذیری کے اعداد و شمار پڑھیں"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ایپ کو کسی اکاؤنٹ کیلئے مطابقت پذیری کے اعداد و شمار، بشمول مطابقت پذیری کے ایونٹس اور جس قدر ڈیٹا مطابقت پذیر ہے اس کی سرگزشت کو پڑھنے کی اجازت دیتا ہے۔"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"آپ USB سٹوریج کے مواد کو پڑھیں"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"اپنے SD کارڈ کے مواد کو پڑھیں"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"ایپ کو آپ کے USB اسٹوریج کے مواد کو پڑھنے کی اجازت دیتا ہے۔"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"ایپ کو آپ کے SD کارڈ کے مواد کو پڑھنے کی اجازت دیتا ہے۔"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"اپنے USB سٹوریج کے مواد میں ترمیم یا حذف کریں"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"اپنے SD کارڈ کے مواد میں ترمیم یا انہیں حذف کریں"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ایپ کو USB اسٹوریج پر لکھنے کی اجازت دیتا ہے۔"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"ایپ کو SD کارڈ پر لکھنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"اپنے اشتراک کردہ اسٹوریج کا مواد پڑھیں"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"ایپ کو آپ کے اشتراک کردہ اسٹوریج کے مواد کو پڑھنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"اپنے اشتراک کردہ اسٹوریج کے مواد میں ترمیم کریں یا اسے حذف کریں"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"ایپ کو آپ کے اشتراک کردہ اسٹوریج کے مواد کو لکھنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP کالز کریں/موصول کریں"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"ایپ کو SIP کالز کرنے اور موصول کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"نئے ٹیلی کام SIM کنکشنز رجسٹر کریں"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"منتخب کردہ وقت کے لیے ایونٹ شیٹول کریں"</string>
<string name="view_flight" msgid="7691640491425680214">"پتہ لگائیں"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"منتخب کردہ پرواز ٹریک کریں"</string>
+ <string name="translate" msgid="9218619809342576858">"ترجمہ کریں"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"منتخب کردہ متن کا ترجمہ کریں"</string>
+ <string name="define" msgid="7394820043869954211">"وضاحت کریں"</string>
+ <string name="define_desc" msgid="7910883642444919726">"منتخب کردہ متن کی وضاحت کریں"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">1 مماثلت</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"ہو گیا"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB اسٹوریج کو مٹا رہا ہے…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD کارڈ کو مٹا رہا ہے…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"اشتراک کردہ اسٹوریج کو صاف کیا جا رہا ہے…"</string>
<string name="share" msgid="1778686618230011964">"اشتراک کریں"</string>
<string name="find" msgid="4808270900322985960">"تلاش کریں"</string>
<string name="websearch" msgid="4337157977400211589">"ویب تلاش"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS درخواست کو USSD درخواست میں تبدیل کر دیا گیا"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"نئی SS درخواست میں تبدیل کر دیا گیا"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"دفتری پروفائل"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"الرٹ کیا گیا"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"پھیلائیں"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"سکیڑیں"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"پھیلاؤ کو ٹوگل کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 2192ad4..204df4e 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi chaqiruv | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWi-Fi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi chaqiruv"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi chaqiruv"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWi-Fi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"O‘chiq"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi afzalligi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Mobil internet afzalligi"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ilovaga multimedia to‘plamingizdan joylashuv axborotini o‘qishga ruxsat beradi."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ilovasi qurilma qulfini ochmoqchi."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrik sensor ishlamayapti"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Aniqlanmadi"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmoq izi qisman aniqlandi. Qayta urinib ko‘ring."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmoq izi aniqlanmadi. Qayta urinib ko‘ring."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmoq izi skaneri kirlangan. Uni tozalab, keyin qayta urinib ko‘ring."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Barmoq juda sekin harakatlandi. Qayta urinib ko‘ring."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Aniqlanmadi"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Barmoq izi tekshirildi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Yuzingiz aniqlandi"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yuzingiz aniqlandi, tasdiqlash uchun bosing"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Ilovaga hisobning sinxronlash sozlamalarini o‘zgartirish uchun ruxsat beradi. Masalan, bundan \"Odamlar\" ilovasini hisob bilan sinxronlanlash uchun foydalanish mumkin."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"sinxronlash statistikasini o‘qish"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Ilovaga hisobning sinxronlash statistikasini, shu jumladan, sinxronlangan hodisalar tarixi va qancha ma’lumot sinxronlanganligi haqidagi ma’lumotni o‘qishga ruxsat beradi."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB xotiradagi ma’lumotlarni ko‘rish"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD xotira kartasi tarkibidagilarni o‘qish"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Dasturga USB xotiradagi ma’lumotlarini ko‘rib chiqish uchun ruxsat beradi."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Dasturga SD kartadagi ma’lumotlarni ko‘rib chiqishga ruxsat berish."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB xotiradagi ma’lumotlarni o‘zgartirish/o‘chirish"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD xotira kartasi tarkibidagilarni o‘zgartirish yoki o‘chirish"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Ilova USB xotiraga ma’lumot yozishi mumkin."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ilova SD kartaga ma’lumot yozishi mumkin."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"umumiy xotiradagi kontentlarni ochish"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Ilovaga umumiy xotiradagi kontentlarni ochishga ruxsat beradi."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"umumiy xotiradagi kontentlarni tahrirlash yoki oʻchirib tashlash"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Ilovaga umumiy xotiradagi kontentlarga yozish imkonini beradi."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP qo‘ng‘iroqlarini amalga oshirish/qabul qilish"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Ilovaga SIP qo‘ng‘iroqlarini amalga oshirish va qabul qilish uchun ruxsat beradi."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"yangi SIM kartali telekommunikatsiya aloqalarini ro‘yxatga olish"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Belgilangan vaqt uchun tadbirni rejalashtirish"</string>
<string name="view_flight" msgid="7691640491425680214">"Trek"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Belgilangan parvozni kuzatish"</string>
+ <string name="translate" msgid="9218619809342576858">"Tarjima qilish"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Belgilangan matnni tarjima qilish"</string>
+ <string name="define" msgid="7394820043869954211">"Izohini olish"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Belgilangan matnning izohini aniqlash"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Xotirada bo‘sh joy tugamoqda"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ba‘zi tizim funksiyalari ishlamasligi mumkin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -1461,8 +1461,7 @@
<item quantity="one">1 ta natija</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Tayyor"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"USB xotirasi tozalanmoqda…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"SD xotira kartasi tozalanmoqda…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Umumiy xotira tozalanmoqda…"</string>
<string name="share" msgid="1778686618230011964">"Yuborish"</string>
<string name="find" msgid="4808270900322985960">"Topish"</string>
<string name="websearch" msgid="4337157977400211589">"Veb qidiruv"</string>
@@ -1834,8 +1833,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS talabi USSD talabiga almashtirildi"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Yangi SS talabiga almashtirildi"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Ishchi profil"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Ogohlantirildi"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Yoyish"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Yig‘ish"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"ochish yoki yopish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 00e9891..70f3161 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Gọi qua Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWifi <xliff:g id="SPN">%s</xliff:g>"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Gọi qua Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Gọi qua Wi-Fi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Tắt"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Ưu tiên Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Ưu tiên dữ liệu di động"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Ứng dụng <xliff:g id="APP">%s</xliff:g> muốn xác thực."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Không có phần cứng sinh trắc học"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Không nhận dạng được"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Đã phát hiện được một phần vân tay. Vui lòng thử lại."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Cảm biến vân tay bị bẩn. Hãy làm sạch và thử lại."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Di chuyển ngón tay quá chậm. Vui lòng thử lại."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Không nhận dạng được"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Đã xác thực vân tay"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Đã xác thực khuôn mặt"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Đã xác thực khuôn mặt, vui lòng nhấn để xác nhận"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Cho phép ứng dụng sửa đổi cài đặt đồng bộ hóa cho tài khoản. Ví dụ: ứng dụng có thể được sử dụng để cho phép đồng bộ hóa ứng dụng Mọi người với tài khoản."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"đọc thống kê đồng bộ hóa"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Cho phép ứng dụng đọc thống kê đồng bộ hóa cho tài khoản, bao gồm lịch sử của các sự kiện đồng bộ hóa và lượng dữ liệu được đồng bộ hóa."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"đọc nội dung của bộ lưu trữ USB của bạn"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"đọc nội dung của thẻ SD của bạn"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Cho phép ứng dụng đọc nội dung của bộ lưu trữ USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Cho phép ứng dụng đọc nội dung của thẻ SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"sửa đổi hoặc xóa nội dung của bộ lưu trữ USB của bạn"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"sửa đổi hoặc xóa nội dung của thẻ SD của bạn"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Cho phép ứng dụng ghi vào bộ lưu trữ USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Cho phép ứng dụng ghi vào thẻ SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"đọc nội dung của bộ nhớ dùng chung"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Cho phép ứng dụng đọc nội dung của bộ nhớ dùng chung."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"sửa đổi hoặc xóa nội dung của bộ nhớ dùng chung"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Cho phép ứng dụng ghi nội dung của bộ nhớ dùng chung."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"thực hiện/nhận các cuộc gọi qua SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Cho phép ứng dụng thực hiện và nhận các cuộc gọi qua SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"đăng ký kết nối SIM viễn thông mới"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Lên lịch biểu sự kiện cho thời gian đã chọn"</string>
<string name="view_flight" msgid="7691640491425680214">"Theo dõi"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Theo dõi chuyến bay đã chọn"</string>
+ <string name="translate" msgid="9218619809342576858">"Dịch"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Dịch văn bản đã chọn"</string>
+ <string name="define" msgid="7394820043869954211">"Định nghĩa"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Định nghĩa văn bản đã chọn"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 trận đấu</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Xong"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Đang xóa bộ lưu trữ USB..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Đang xóa thẻ SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Đang xóa bộ nhớ dùng chung…"</string>
<string name="share" msgid="1778686618230011964">"Chia sẻ"</string>
<string name="find" msgid="4808270900322985960">"Tìm"</string>
<string name="websearch" msgid="4337157977400211589">"Tìm kiếm trên web"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Yêu cầu SS đã thay đổi thành yêu cầu USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Đã thay đổi thành yêu cầu SS mới"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Hồ sơ công việc"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Đã phát âm báo"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Mở rộng"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Thu gọn"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"chuyển đổi mở rộng"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d655d32..c38406f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> WLAN"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WLAN 通话 | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"WLAN 通话"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"WLAN"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"WLAN 通话"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"关闭"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"首选 WLAN"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"首选移动数据网络"</string>
@@ -296,7 +292,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>发送和查看短信吗?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"存储空间"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"访问您设备上的照片、媒体内容和文件"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问您设备上的照片、媒体内容和文件吗?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”<b></b>访问您设备上的照片、媒体内容和文件吗?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"麦克风"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"录制音频"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>录音吗?"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"允许该应用从您的媒体收藏中读取位置信息。"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g>应用需要进行身份验证。"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"生物识别硬件无法使用"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"无法识别"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"仅检测到部分指纹,请重试。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"无法处理指纹,请重试。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指纹传感器有脏污。请擦拭干净,然后重试。"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"手指移动太慢,请重试。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"无法识别"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"已验证指纹"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"面孔已验证"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"面孔已验证,请按确认按钮"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"允许该应用修改某个帐号的同步设置。例如,此权限可用于在“联系人”应用与某个帐号之间启用同步。"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"读取同步统计信息"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"允许该应用读取某个帐号的同步统计信息,包括同步活动历史记录和同步数据量。"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"读取您的USB存储设备中的内容"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"读取您的SD卡中的内容"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"允许应用读取您USB存储设备中的内容。"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"允许应用读取您SD卡的内容。"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"修改或删除您的USB存储设备中的内容"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"修改或删除您的SD卡中的内容"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"允许应用写入USB存储设备。"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允许应用写入SD卡。"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"读取您共享存储空间中的内容"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"允许该应用读取您共享存储空间中的内容。"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"修改或删除您共享存储空间中的内容"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"允许该应用写入您共享存储空间中的内容。"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"拨打/接听SIP电话"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"允许该应用拨打和接听SIP电话。"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"注册新的电信 SIM 卡连接"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"将活动安排在所选时间"</string>
<string name="view_flight" msgid="7691640491425680214">"跟踪"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"跟踪所选航班"</string>
+ <string name="translate" msgid="9218619809342576858">"翻译"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"翻译所选文字"</string>
+ <string name="define" msgid="7394820043869954211">"定义"</string>
+ <string name="define_desc" msgid="7910883642444919726">"定义所选文字"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 条结果</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"完成"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"正在清除USB存储设备的数据..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"正在清除SD卡的数据..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"正在清空共享的存储空间…"</string>
<string name="share" msgid="1778686618230011964">"分享"</string>
<string name="find" msgid="4808270900322985960">"查找"</string>
<string name="websearch" msgid="4337157977400211589">"网页搜索"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS 请求已更改为 USSD 请求"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"已更改为新的 SS 请求"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"工作资料"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"已提醒"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"展开"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"收起"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"切换展开模式"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 086a068..f59cfcb 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"WiFi 通話 | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"關閉"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"首選 Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"流動數據優先"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取媒體集的位置。"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"「<xliff:g id="APP">%s</xliff:g>」應用程式需要驗證。"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"無法使用生物識別硬件"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"未能識別"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"只偵測到部分指紋。請再試一次。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋。請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器不乾淨。請清潔後再試一次。"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"手指移動太慢,請重試。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"未能識別"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"驗證咗指紋"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"面孔已經驗證"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"面孔已經驗證,請㩒一下 [確認]"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"允許應用程式修改帳戶的同步設定,例如讓「通訊錄」應用程式與某個帳戶保持同步。"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"讀取同步處理統計資料"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"允許應用程式讀取帳戶的同步統計資料,包括同步活動記錄,以及保持同步的資料量。"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"讀取您 USB 儲存裝置中的內容"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"讀取您 SD 記憶卡中的內容"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"允許應用程式讀取 USB 儲存裝置的內容。"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"允許應用程式讀取 SD 卡的內容。"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"修改或刪除您 USB 儲存裝置中的內容"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"修改或刪除您 SD 記憶卡中的內容"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"允許應用程式寫入 USB 儲存裝置。"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允許應用程式寫入 SD 記憶卡。"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"讀取您共用儲存空間的內容"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"允許應用程式讀取您共用儲存空間的內容。"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"修改或刪除您共用儲存空間的內容"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"允許應用程式寫入您共用儲存空間的內容。"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"撥打/接聽 SIP 電話"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"允許應用程式撥打及接聽 SIP 電話。"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"註冊新的電訊 SIM 卡連接"</string>
@@ -1095,7 +1091,7 @@
<string name="map_desc" msgid="1836995341943772348">"搵出指定地址"</string>
<string name="browse" msgid="1245903488306147205">"開啟"</string>
<string name="browse_desc" msgid="8220976549618935044">"打開指定網址"</string>
- <string name="sms" msgid="4560537514610063430">"短訊"</string>
+ <string name="sms" msgid="4560537514610063430">"發短訊"</string>
<string name="sms_desc" msgid="7526588350969638809">"Send 短訊去指定電話號碼"</string>
<string name="add_contact" msgid="7867066569670597203">"新增"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"加入通訊錄"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"將活動安排喺指定時間"</string>
<string name="view_flight" msgid="7691640491425680214">"追蹤"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"追蹤指定航班"</string>
+ <string name="translate" msgid="9218619809342576858">"翻譯"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"翻譯揀咗嘅文字"</string>
+ <string name="define" msgid="7394820043869954211">"定義"</string>
+ <string name="define_desc" msgid="7910883642444919726">"定義揀左嘅文字"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 個相符項目</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"完成"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"正在清除 USB 儲存裝置資料..."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"正在清除 SD 記憶卡資料..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"正在清除共用儲存空間資料…"</string>
<string name="share" msgid="1778686618230011964">"分享"</string>
<string name="find" msgid="4808270900322985960">"尋找"</string>
<string name="websearch" msgid="4337157977400211589">"Google 網頁搜尋"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS 要求已變更為 USSD 要求"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"已變更為新的 SS 要求"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"工作設定檔"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"已提醒"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"展開"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"收合"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"切換展開"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index e789e9a..489dfea 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi 通話 | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"關閉"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi 優先"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"行動網路優先"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取你的媒體收藏的位置資訊。"</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"「<xliff:g id="APP">%s</xliff:g>」應用程式需要驗證使用者身分。"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"無法使用生物特徵辨識硬體"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"無法辨識"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"僅偵測到部分指紋,請再試一次。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋,請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器有髒汙。請清潔感應器,然後再試一次。"</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"手指移動速度過慢,請再試一次。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"無法辨識"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"指紋驗證成功"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"臉孔驗證成功"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"臉孔驗證成功,請按下 [確認] 按鈕"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"允許應用程式修改帳戶的同步處理設定,例如讓「使用者」應用程式與某個帳戶進行同步處理。"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"讀取同步處理狀態"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"允許應用程式讀取帳戶的同步處理統計資料,包括同步處理活動記錄,以及同步處理的資料量。"</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"讀取 USB 儲存裝置的內容"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"讀取 SD 卡的內容"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"允許應用程式讀取 USB 儲存裝置的內容。"</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"允許應用程式讀取 SD 卡的內容。"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"修改或刪除 USB 儲存裝置的內容"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"修改或刪除 SD 卡的內容"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"允許應用程式寫入 USB 儲存裝置。"</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允許應用程式寫入 SD 卡。"</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"讀取共用儲存空間中的內容"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"允許這個應用程式讀取共用儲存空間中的內容。"</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"修改或刪除共用儲存空間中的內容"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"允許這個應用程式寫入共用儲存空間中的內容。"</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"撥打/接聽 SIP 通話"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"允許應用程式撥打及接聽 SIP 通話。"</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"註冊新的電信 SIM 卡連線"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"將活動安排在所選時間"</string>
<string name="view_flight" msgid="7691640491425680214">"追蹤"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"追蹤所選航班"</string>
+ <string name="translate" msgid="9218619809342576858">"翻譯"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"翻譯所選文字"</string>
+ <string name="define" msgid="7394820043869954211">"定義"</string>
+ <string name="define_desc" msgid="7910883642444919726">"定義所選文字"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1460,8 +1460,7 @@
<item quantity="one">1 個相符項目</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"完成"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"正在清除 USB 儲存裝置的資料…"</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"正在清除 SD 卡的資料…"</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"正在清除共用儲存空間…"</string>
<string name="share" msgid="1778686618230011964">"分享"</string>
<string name="find" msgid="4808270900322985960">"尋找"</string>
<string name="websearch" msgid="4337157977400211589">"網頁搜尋"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS 要求已變更為 USSD 要求"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"已變更為新的 SS 要求"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"工作資料夾"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"已提醒"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"展開"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"收合"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"切換展開模式"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 2e1540f..18a3db4 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -136,14 +136,10 @@
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Ukushaya kwe-WiFi | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <!-- no translation found for wfcSpnFormat_wifi_calling (4990486735013125329) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi (1892673884655959773) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_wifi_calling_wo_hyphen (1336669776254502831) -->
- <skip />
- <!-- no translation found for wfcSpnFormat_vowifi (1765176406171272629) -->
- <skip />
+ <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Ukushaya kwe-Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"I-Wi-Fi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Ukushaya kwe-WiFi"</string>
+ <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Valiwe"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Kuncanyelwa i-Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Kuncanyelwa iselula"</string>
@@ -533,6 +529,11 @@
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ivumela uhlelo lokusebenza ukuthi lifunde izindawo kusukela kuqoqo lakho lemidiya."</string>
<string name="biometric_dialog_default_title" msgid="4229778503907743328">"Uhlelo lokusebenza lwe-<xliff:g id="APP">%s</xliff:g> lifuna ukufakazela ubuqiniso."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"I-Biometric hardware ayitholakali"</string>
+ <!-- no translation found for biometric_error_user_canceled (2260175018114348727) -->
+ <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Akwaziwa"</string>
+ <!-- no translation found for biometric_error_canceled (349665227864885880) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Izigxivizo zeminwe ezincane zitholiwe. Sicela uzame futhi."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ayikwazanga ukucubungula izigxivizo zeminwe. Sicela uzame futhi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Inzwa yezigxivizo zeminwe ingcolile. Sicela uyihlanze uphinde uzame futhi."</string>
@@ -540,7 +541,6 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Umnwe uhanjiswe kancane kakhulu. Sicela uzame futhi."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="biometric_not_recognized" msgid="5770511773560736082">"Akwaziwa"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Isingxivizo somunwe sigunyaziwe"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Ubuso bufakazelwe ubuqiniso"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Ukuqinisekiswa kobuso, sicela ucindezele okuthi qinisekisa"</string>
@@ -596,14 +596,10 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Ivumela uhlelo lokusebenza ukushintsha izilungiselelo zokuvumelanisa ze-akhawunti. Isibonelo, lokhu kungasetshenziswa ukunika amandla ukuvumelanisa kohlelo lokusebenza le-People ne-akhawunti."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"funda izibalo zokuvumelanisa"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Ivumela uhlelo lokusebenza ukufunda izibalo zokuvumelanisa ze-akhawunti, kufaka phakathi umlando wezehlakalo ezivumelanisiwe nokuthi ingakanani idatha evumelanisiwe."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"funda okuqukethwe kokugciniwe kwakho okufinyeleleka nge-USD"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"funda okuqukethwe ekhadini lakho le-SD"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Ivumela uhlelo lokusebenza ukufunda okuqukethwe kwesitoreji sakho se-USB."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Ivumela uhlelo lokusebenza ukufunda okuqukethwe kwekhadi lakho le-SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"guqula noma ususe okuqukethwe kwakho okugciniwe okufinyeleleka nge-USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"shintsha noma ususe okuqukethwe ekhadini lakho le-SD"</string>
- <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Ivumela uhlelo lokusebenza ukuthi ibhalele ekulondolozweni kwe-USB."</string>
- <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ivumela uhlelo lokusebenza ukuthi ibhalele ekhadini le-SD."</string>
+ <string name="permlab_sdcardRead" msgid="1438933556581438863">"funda okuqukethwe kwesitoreji sakho esabiwe"</string>
+ <string name="permdesc_sdcardRead" msgid="1804941689051236391">"Ivumela uhlelo lokusebenza ukuthi lufunde okuqukethwe kwesitoreji sakho esabiwe."</string>
+ <string name="permlab_sdcardWrite" msgid="9220937740184960897">"guqula noma susa okuqukethwe kwesitoreji sakho esabiwe"</string>
+ <string name="permdesc_sdcardWrite" msgid="2834431057338203959">"Ivumela uhlelo lokusebenza ukuthi lubhale okuqukethwe kwesitoreji sakho esabiwe."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"yenza/thola amakholi we-SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Ivumela uhlelo lokusebenza ukwenza nokuthola amakholi we-SIP."</string>
<string name="permlab_register_sim_subscription" msgid="3166535485877549177">"bhalisa uxhumo le-SIM le-telecom olusha"</string>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Shejula imicimbi yesikhathi esikhethiwe"</string>
<string name="view_flight" msgid="7691640491425680214">"Landelela"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Ithrekhi ikhethe indiza"</string>
+ <string name="translate" msgid="9218619809342576858">"Humusha"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Humusha umbhalo ohumushiwe"</string>
+ <string name="define" msgid="7394820043869954211">"Chaza"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Chaza umbhalo okhethiwe"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
@@ -1460,8 +1460,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> kokungu-<xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Kwenziwe"</string>
- <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Isula isitoreji se-USB."</string>
- <string name="progress_erasing" product="default" msgid="6596988875507043042">"Isula ikhadi le-SD..."</string>
+ <string name="progress_erasing" msgid="2569962663843586562">"Isusa isitoreji esabiwe…"</string>
<string name="share" msgid="1778686618230011964">"Yabelana"</string>
<string name="find" msgid="4808270900322985960">"Thola"</string>
<string name="websearch" msgid="4337157977400211589">"USesho lweWebhu"</string>
@@ -1833,8 +1832,7 @@
<string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"Isicelo se-SS sishintshele kusicelo se-USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Ishintshele kusicelo esisha se-SS"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Iphrofayela yomsebenzi"</string>
- <!-- no translation found for notification_alerted_content_description (1296617716556420585) -->
- <skip />
+ <string name="notification_alerted_content_description" msgid="1296617716556420585">"Kuxwayisiwe"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"Nweba"</string>
<string name="expand_button_content_description_expanded" msgid="8520652707158554895">"Goqa"</string>
<string name="expand_action_accessibility" msgid="5307730695723718254">"guqula ukunwebisa"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f55e48e..8dbea38 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6034,7 +6034,7 @@
<attr name="insetBottom" format="fraction|dimension" />
</declare-styleable>
- <!-- Drawable used to draw animated images (gif) -->
+ <!-- Drawable used to draw animated images (gif). -->
<declare-styleable name="AnimatedImageDrawable">
<!-- Identifier of the image file. This attribute is mandatory.
It must be an image file with multiple frames, e.g. gif or webp -->
@@ -8918,8 +8918,8 @@
<attr name="name" />
</declare-styleable>
- <!-- Attributes that are read when parsing a <font> tag, which is a child of
- <font-family>. This represents an actual font file and its attributes. -->
+ <!-- Attributes that are read when parsing a <font> tag, which is a child of
+ <font-family>. This represents an actual font file and its attributes. -->
<declare-styleable name="FontFamilyFont">
<!-- The style of the given font file. This will be used when the font is being loaded into
the font stack and will override any style information in the font's header tables. If
@@ -8950,7 +8950,7 @@
<attr name="fontVariationSettings" format="string" />
</declare-styleable>
- <!-- Attributes that are read when parsing a <fontfamily> tag. -->
+ <!-- Attributes that are read when parsing a <fontfamily> tag. -->
<declare-styleable name="FontFamily">
<!-- The authority of the Font Provider to be used for the request. -->
<attr name="fontProviderAuthority" format="string" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 089c59f..35263a3 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -268,6 +268,12 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system default text classifier -->
<flag name="textClassifier" value="0x10000" />
+ <!-- Additional flag from base permission type: this permission will be granted to the
+ wellbeing app, as defined by the OEM. -->
+ <flag name="wellbeing" value="0x20000" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the document manager -->
+ <flag name="documenter" value="0x40000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
@@ -1394,6 +1400,29 @@
<attr name="usesNonSdkApi" format="boolean" />
+ <!-- Specify the type of foreground service. Apps targeting API
+ {@link android.os.Build.VERSION_CODES#Q} or later must specify foreground service type,
+ otherwise a SecurityException is thrown when
+ {@link android.app.Service#startForeground(int, Notification)} on this service is called.
+ -->
+ <attr name="foregroundServiceType">
+ <!-- Data (photo, file, account) upload/download, backup/restore, import/export, fetch,
+ transfer over network between device and cloud. -->
+ <enum name="sync" value="1" />
+ <!-- Music, video, news or other media play. -->
+ <enum name="mediaPlay" value="2" />
+ <!-- Ongoing phone call or video conference. -->
+ <enum name="phoneCall" value="3" />
+ <!-- GPS, map, navigation location update. -->
+ <enum name="location" value="4" />
+ <!-- Auto, bluetooth, TV or other devices connection, monitoring and interaction. -->
+ <enum name="deviceCompanion" value="5" />
+ <!-- Process that should not be interrupted, including installation, setup, photo
+ compression etc. -->
+ <enum name="ongoingProcess" value="6" />
+ </attr>
+
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1574,6 +1603,9 @@
<!-- Declares that this application should be invoked without non-SDK API enforcement -->
<attr name="usesNonSdkApi" />
+ <!-- If {@code true} the user is prompted to keep the app's data on uninstall -->
+ <attr name="hasFragileUserData" />
+
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
@@ -2242,6 +2274,8 @@
recommended to measure memory usage under typical workloads to determine
whether it makes sense to use this flag. -->
<attr name="useAppZygote" format="boolean" />
+ <!-- If this is a foreground service, specify its category. -->
+ <attr name="foregroundServiceType" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1d80961..97a21a5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -222,6 +222,11 @@
so that applications can still use their own mechanisms. -->
<bool name="config_enableAutoPowerModes">false</bool>
+ <!-- Whether (if true) this is a kind of device that can be moved around (eg. phone/laptop),
+ or (if false) something for which movement is either not measurable or should not count
+ toward power states (eg. tv/soundbar). -->
+ <bool name="config_autoPowerModeUseMotionSensor">true</bool>
+
<!-- The threshold angle for any motion detection in auto-power save modes.
In hundreths of a degree. -->
<integer name="config_autoPowerModeThresholdAngle">200</integer>
@@ -2125,6 +2130,9 @@
during initialization when the setting is still null. -->
<bool name="config_dozeAlwaysOnEnabled">true</bool>
+ <!-- If AOD can show an ambient version of the wallpaper -->
+ <bool name="config_dozeSupportsAodWallpaper">true</bool>
+
<!-- Whether the display blanks itself when transitioning from a doze to a non-doze state -->
<bool name="config_displayBlanksAfterDoze">false</bool>
@@ -3346,6 +3354,29 @@
See android.view.textclassifier.TextClassificationManager.
-->
<string name="config_defaultTextClassifierPackage" translatable="false"></string>
+
+ <!-- The package name for the default wellbeing app.
+ This package must be trusted, as it has the permissions to control other applications
+ on the device.
+ Example: "com.android.wellbeing"
+ -->
+ <string name="config_defaultWellbeingPackage" translatable="false"></string>
+
+ <!-- The package name for the system's content capture service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, content capture will be
+ disabled.
+ Example: "com.android.contentcapture/.ContentcaptureService"
+ -->
+ <string name="config_defaultContentCaptureService" translatable="false"></string>
+
+ <!-- The package name for the system's augmented autofill service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, augmented autofill wil be
+ disabled.
+ Example: "com.android.augmentedautofill/.AugmentedAutofillService"
+ -->
+ <string name="config_defaultAugmentedAutofillService" translatable="false"></string>
<!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
<bool name="config_useDefaultFocusHighlight">true</bool>
@@ -3484,7 +3515,13 @@
<!-- Controls whether system buttons use all caps for text -->
<bool name="config_buttonTextAllCaps">true</bool>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
- <string name="config_headlineFontFamilyMedium">@string/font_family_button_material</string>
+ <string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
+ <!-- Name of a font family to use for body text. -->
+ <string name="config_bodyFontFamily" translatable="false">sans-serif</string>
+ <!-- Name of a font family to use for light body text. -->
+ <string name="config_bodyFontFamilyLight" translatable="false">sans-serif-light</string>
+ <!-- Name of a font family to use for medium body text. -->
+ <string name="config_bodyFontFamilyMedium" translatable="false">sans-serif-medium</string>
<!-- Size of icon shown beside a preference locked by admin -->
<dimen name="config_restrictedIconSize">@dimen/restricted_icon_size_material</dimen>
@@ -3560,4 +3597,9 @@
<!-- Component name for default assistant on this device -->
<string name="config_defaultAssistantComponentName">#+UNSET</string>
+ <!-- Class name for the InputEvent compatibility processor override.
+ Empty string means use the default compatibility processor
+ (android.view.InputEventCompatProcessor). -->
+ <string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string>
+
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5e8af62..eac8b48 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2929,6 +2929,8 @@
<public name="dataUsedForMonetization" />
<public name="dataRetentionTime" />
<public name="selectionDividerHeight" />
+ <public name="foregroundServiceType" />
+ <public name="hasFragileUserData" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
@@ -2969,6 +2971,8 @@
<public-group type="dimen" first-id="0x01050007">
<!-- @hide @SystemApi -->
<public name="config_restrictedIconSize" />
+ <!-- @hide @SystemApi -->
+ <public name="config_mediaMetadataBitmapMaxSize" />
</public-group>
<!-- ===============================================================
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a33f6b2..cab01f9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1215,6 +1215,15 @@
<string name="permdesc_manageOwnCalls">Allows the app to route its calls through the system in
order to improve the calling experience.</string>
+ <!-- Title of an application permission. When granted the app is allowed to be enabled as
+ a companion app. [CHAR LIMIT=NONE]-->
+ <string name="permlab_callCompanionApp">see and control calls through the system.</string>
+ <!-- Description of an application permission. When granted the app is allowed to be enabled as
+ a companion app. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_callCompanionApp">Allows the app to see and control ongoing calls on the
+ device. This includes information such as call numbers for calls and the state of the
+ calls.</string>
+
<!-- Title of an application permission. When granted the user is giving access to a third
party app to continue a call which originated in another app. For example, the user
could be in a voice call over their carrier's mobile network, and a third party video
@@ -1397,6 +1406,16 @@
re-enables the keylock when the call is finished.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permlab_getAndRequestScreenLockComplexity">get and request screen lock complexity</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permdesc_getAndRequestScreenLockComplexity">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.
+ </string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permlab_useBiometric">use biometric hardware</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
<string name="permdesc_useBiometric">Allows the app to use biometric hardware for authentication</string>
@@ -3323,6 +3342,15 @@
<!-- Notification action name for opening the wifi picker, showing the user all the nearby networks. -->
<string name="wifi_available_action_all_networks">All networks</string>
+ <!-- Notification title for a connection to a app suggested wireless network.-->
+ <string name="wifi_suggestion_title">Connected to Wi\u2011Fi network proposed by <xliff:g id="name" example="App123">%s</xliff:g></string>
+ <!-- Notification content for a connection to a app suggested wireless network.-->
+ <string name="wifi_suggestion_content">Do you want to let <xliff:g id="name" example="App123">%s</xliff:g> propose networks for you?</string>
+ <!-- Notification action for allowing app specified in the notification body.-->
+ <string name="wifi_suggestion_action_allow_app">Yes</string>
+ <!-- Notification action for disallowing app specified in the notification body.-->
+ <string name="wifi_suggestion_action_disallow_app">No</string>
+
<!--Notification title for Wi-Fi Wake onboarding. This is displayed the first time a user disables Wi-Fi with the feature enabled. -->
<string name="wifi_wakeup_onboarding_title">Wi\u2011Fi will turn on automatically</string>
<!--Notification subtext for Wi-Fi Wake onboarding.-->
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index d722961..2c04ec8 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -113,6 +113,15 @@
<style name="Widget.DeviceDefault.ListView.White" parent="Widget.Material.ListView.White"/>
<style name="Widget.DeviceDefault.MediaRouteButton" parent="Widget.Material.MediaRouteButton" />
<style name="Widget.DeviceDefault.NumberPicker" parent="Widget.Material.NumberPicker"/>
+ <style name="Widget.DeviceDefault.Notification.Text" parent="Widget.Material.Notification.Text">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
+ </style>
+ <style name="Widget.DeviceDefault.Notification.MessagingText" parent="Widget.Material.Notification.MessagingText">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
+ </style>
+ <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item>
+ </style>
<style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
<style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
<style name="Widget.DeviceDefault.ProgressBar.Large.Inverse" parent="Widget.Material.ProgressBar.Large.Inverse"/>
@@ -218,41 +227,100 @@
<style name="Widget.DeviceDefault.Light.TimePicker" parent="Widget.Material.Light.TimePicker"/>
<!-- Text Appearance Styles -->
- <style name="TextAppearance.DeviceDefault" parent="TextAppearance.Material"/>
- <style name="TextAppearance.DeviceDefault.Inverse" parent="TextAppearance.Material.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Large" parent="TextAppearance.Material.Large"/>
- <style name="TextAppearance.DeviceDefault.Large.Inverse" parent="TextAppearance.Material.Large.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Medium" parent="TextAppearance.Material.Medium"/>
- <style name="TextAppearance.DeviceDefault.Medium.Inverse" parent="TextAppearance.Material.Medium.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Small" parent="TextAppearance.Material.Small"/>
- <style name="TextAppearance.DeviceDefault.Small.Inverse" parent="TextAppearance.Material.Small.Inverse"/>
- <style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Material.SearchResult.Title"/>
- <style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Material.SearchResult.Subtitle"/>
- <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget"/>
+ <style name="TextAppearance.DeviceDefault" parent="TextAppearance.Material">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Inverse" parent="TextAppearance.Material.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Large" parent="TextAppearance.Material.Large">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Large.Inverse" parent="TextAppearance.Material.Large.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Medium" parent="TextAppearance.Material.Medium">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Medium.Inverse" parent="TextAppearance.Material.Medium.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Small" parent="TextAppearance.Material.Small">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Small.Inverse" parent="TextAppearance.Material.Small.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Material.SearchResult.Title">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Material.SearchResult.Subtitle">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Notification" parent="TextAppearance.Material.Notification">
+ <item name="fontFamily">@string/config_headlineFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
+ <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Widget.Button" parent="TextAppearance.Material.Widget.Button">
<item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
<item name="textAllCaps">@bool/config_buttonTextAllCaps</item>
</style>
- <style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Material.Widget.IconMenu.Item"/>
- <style name="TextAppearance.DeviceDefault.Widget.TabWidget" parent="TextAppearance.Material.Widget.TabWidget"/>
- <style name="TextAppearance.DeviceDefault.Widget.TextView" parent="TextAppearance.Material.Widget.TextView"/>
- <style name="TextAppearance.DeviceDefault.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Widget.TextView.PopupMenu"/>
- <style name="TextAppearance.DeviceDefault.Widget.DropDownHint" parent="TextAppearance.Material.Widget.DropDownHint"/>
- <style name="TextAppearance.DeviceDefault.Widget.DropDownItem" parent="TextAppearance.Material.Widget.DropDownItem"/>
- <style name="TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Widget.TextView.SpinnerItem"/>
- <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText"/>
+ <style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Material.Widget.IconMenu.Item">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.TabWidget" parent="TextAppearance.Material.Widget.TabWidget">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.TextView" parent="TextAppearance.Material.Widget.TextView">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Widget.TextView.PopupMenu">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.DropDownHint" parent="TextAppearance.Material.Widget.DropDownHint">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.DropDownItem" parent="TextAppearance.Material.Widget.DropDownItem">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Widget.TextView.SpinnerItem">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored" parent="TextAppearance.DeviceDefault.Widget.Button">
<item name="textColor">@color/btn_colored_borderless_text_material</item>
</style>
- <style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Material.Widget.PopupMenu"/>
- <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Material.Widget.PopupMenu.Large"/>
- <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Material.Widget.PopupMenu.Small"/>
+ <style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Material.Widget.PopupMenu">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Material.Widget.PopupMenu.Large">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Material.Widget.PopupMenu.Small">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Material.Widget.ActionBar.Title">
<item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
</style>
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
<style name="TextAppearance.DeviceDefault.WindowTitle" parent="TextAppearance.Material.WindowTitle">
<item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
</style>
@@ -260,15 +328,25 @@
<item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
</style>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Title.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Title.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Title.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Title.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu"/>
- <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Title" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Title" />
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Title" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Title"/>
<!-- Preference Styles -->
<style name="Preference.DeviceDefault" parent="Preference.Material"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 67b3c92..5a7199d 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -1313,4 +1313,5 @@
<item name="gravity">top|center_horizontal</item>
</style>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 783f1f3..161e416 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -265,6 +265,7 @@
<java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" />
<java-symbol type="bool" name="config_autoPowerModePreferWristTilt" />
<java-symbol type="bool" name="config_autoPowerModePrefetchLocation" />
+ <java-symbol type="bool" name="config_autoPowerModeUseMotionSensor" />
<java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
<java-symbol type="bool" name="config_enable_puk_unlock_screen" />
<java-symbol type="bool" name="config_disableLockscreenByDefault" />
@@ -301,6 +302,7 @@
<java-symbol type="bool" name="config_enableWallpaperService" />
<java-symbol type="bool" name="config_checkWallpaperAtBoot" />
<java-symbol type="string" name="config_wallpaperManagerServiceName" />
+ <java-symbol type="string" name="config_inputEventCompatProcessorOverrideClassName" />
<java-symbol type="bool" name="config_enableUpdateableTimeZoneRules" />
<java-symbol type="bool" name="config_timeZoneRulesUpdateTrackingEnabled" />
<java-symbol type="string" name="config_timeZoneRulesUpdaterPackage" />
@@ -1988,6 +1990,10 @@
<java-symbol type="string" name="wifi_available_content_failed_to_connect" />
<java-symbol type="string" name="wifi_available_action_connect" />
<java-symbol type="string" name="wifi_available_action_all_networks" />
+ <java-symbol type="string" name="wifi_suggestion_title" />
+ <java-symbol type="string" name="wifi_suggestion_content" />
+ <java-symbol type="string" name="wifi_suggestion_action_allow_app" />
+ <java-symbol type="string" name="wifi_suggestion_action_disallow_app" />
<java-symbol type="string" name="wifi_wakeup_onboarding_title" />
<java-symbol type="string" name="wifi_wakeup_onboarding_subtext" />
<java-symbol type="string" name="wifi_wakeup_onboarding_action_disable" />
@@ -3265,6 +3271,9 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
+ <java-symbol type="string" name="config_defaultWellbeingPackage" />
+ <java-symbol type="string" name="config_defaultContentCaptureService" />
+ <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
<java-symbol type="string" name="notification_channel_foreground_service" />
<java-symbol type="string" name="foreground_service_app_in_background" />
@@ -3310,6 +3319,7 @@
<java-symbol type="integer" name="config_autoGroupAtCount" />
<java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
<java-symbol type="bool" name="config_dozeAlwaysOnEnabled" />
+ <java-symbol type="bool" name="config_dozeSupportsAodWallpaper" />
<java-symbol type="bool" name="config_displayBlanksAfterDoze" />
<java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index fa009bd..fec101a 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1724,4 +1724,10 @@
<item name="layout_gravity">center</item>
</style>
+ <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
+ </style>
+
+ <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient">
+ </style>
+
</resources>
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index d289f1f..9b5b725 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -16,9 +16,16 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import android.app.admin.PasswordMetrics.PasswordComplexityBucket;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -164,4 +171,126 @@
}
+
+ @Test
+ public void testConstructQuality() {
+ PasswordMetrics expected = new PasswordMetrics();
+ expected.quality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+
+ PasswordMetrics actual = new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testDetermineComplexity_none() {
+ assertEquals(PASSWORD_COMPLEXITY_NONE,
+ PasswordMetrics.computeForPassword("").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowSomething() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ new PasswordMetrics(PASSWORD_QUALITY_SOMETHING).determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowNumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("1234").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowNumericComplex() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("124").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowAlphabetic() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("a!").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowAlphanumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("a!1").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_mediumNumericComplex() {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
+ PasswordMetrics.computeForPassword("1238").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_mediumAlphabetic() {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
+ PasswordMetrics.computeForPassword("ab!c").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_mediumAlphanumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
+ PasswordMetrics.computeForPassword("ab!1").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_highNumericComplex() {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH,
+ PasswordMetrics.computeForPassword("12389647!").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_highAlphabetic() {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH,
+ PasswordMetrics.computeForPassword("alphabetic!").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_highAlphanumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH,
+ PasswordMetrics.computeForPassword("alphanumeric123!").determineComplexity());
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_none() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_NONE).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_NONE, metrics.determineComplexity());
+ }
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_low() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_LOW).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_LOW, metrics.determineComplexity());
+ }
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_medium() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_MEDIUM).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metrics.determineComplexity());
+ }
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_high() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_HIGH).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH, metrics.determineComplexity());
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index 1f047f9e..28aaf1e 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -16,18 +16,22 @@
package android.app.usage;
-import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.app.usage.UsageEvents.Event;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -120,10 +124,10 @@
left.mBeginTimeStamp = 100000;
left.mTotalTimeInForeground = 10;
- left.mLastForegroundActivityEventMap.put("com.test.activity1", MOVE_TO_FOREGROUND);
- left.mLastForegroundActivityEventMap.put("com.test.activity2", MOVE_TO_FOREGROUND);
- left.mLastForegroundServiceEventMap.put("com.test.service1", FOREGROUND_SERVICE_START);
- left.mLastForegroundServiceEventMap.put("com.test.service2", FOREGROUND_SERVICE_START);
+ left.mActivities.put(1, Event.ACTIVITY_RESUMED);
+ left.mActivities.put(2, Event.ACTIVITY_RESUMED);
+ left.mForegroundServices.put("com.test.service1", FOREGROUND_SERVICE_START);
+ left.mForegroundServices.put("com.test.service2", FOREGROUND_SERVICE_START);
Parcel p = Parcel.obtain();
left.writeToParcel(p, 0);
@@ -133,37 +137,38 @@
}
@Test
- public void testForegroundActivity() {
+ public void testActivity() {
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND);
+ left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 200000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mLaunchCount, 1);
+ assertEquals(left.mTotalTimeInForeground, 0);
+ assertEquals(left.mTotalTimeVisible, 0);
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertFalse(left.mLastForegroundActivityEventMap.containsKey("com.test.activity1"));
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
- }
+ assertEquals(left.mTotalTimeVisible, 350000 - 200000);
- @Test
- public void testEvent_CONTINUE_PREVIOUS_DAY() {
- left.mPackageName = "com.test";
- left.mBeginTimeStamp = 100000;
-
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
- assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 400000 - 200000);
+
+ left.update("com.test.activity1", 500000, ACTIVITY_DESTROYED, 1);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
+ assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 400000 - 200000);
}
@Test
@@ -171,93 +176,143 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update(null, 350000, END_OF_DAY);
- assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(END_OF_DAY));
- assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
- }
-
- @Test
- public void testForegroundActivityEventSequence() {
- left.mPackageName = "com.test";
- left.mBeginTimeStamp = 100000;
-
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
- assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
- assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
-
- left.update("com.test.activity1", 450000, MOVE_TO_FOREGROUND);
- assertEquals(left.mLastTimeUsed, 450000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
- assertEquals(left.mTotalTimeInForeground, 250000);
-
- left.update("com.test.activity1", 500000, MOVE_TO_BACKGROUND);
- assertEquals(left.mLastTimeUsed, 500000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
- assertEquals(left.mTotalTimeInForeground, 250000 + 50000 /*500000 - 450000*/);
- }
-
- @Test
- public void testForegroundActivityEventOutOfSequence() {
- left.mPackageName = "com.test";
- left.mBeginTimeStamp = 100000;
-
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update("com.test.activity1", 150000, MOVE_TO_FOREGROUND);
- assertEquals(left.mLastTimeUsed, 150000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mLaunchCount, 1);
- assertEquals(left.mTotalTimeInForeground, 50000 /*150000 - 100000*/);
- left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND);
+ left.update(null, 350000, END_OF_DAY, 0);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 350000 - 100000);
+ }
+
+ @Test
+ public void testEvent_ACTIVITY_PAUSED() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, ACTIVITY_PAUSED, 1);
+ assertEquals(left.mLastTimeUsed, 0);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
+
+ left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 200000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mTotalTimeInForeground, 0);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+
+ left.update("com.test.activity1", 300000, ACTIVITY_PAUSED, 1);
+ assertEquals(left.mLastTimeUsed, 300000);
+ assertEquals(left.mLastTimeVisible, 300000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
+ assertEquals(left.mTotalTimeInForeground, 300000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 300000 - 100000);
+
+ left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1);
+ assertEquals(left.mLastTimeUsed, 300000);
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 300000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 400000 - 100000);
+ }
+
+ @Test
+ public void testEvent_CHANGE_TO_INVISIBLE() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED);
+
+ left.update("com.test.activity1", 200000, ACTIVITY_STOPPED, 1);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+
+ left.update("com.test.activity1", 300000, ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 300000);
+ assertEquals(left.mLastTimeVisible, 300000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+ }
+
+ @Test
+ public void testEvent_ACTIVITY_DESTROYED() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED);
+
+ left.update("com.test.activity1", 200000, ACTIVITY_DESTROYED, 1);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+ }
+
+ @Test
+ public void testActivityEventOutOfOrder() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mLaunchCount, 1);
+ assertEquals(left.mTotalTimeInForeground, 0);
+ assertEquals(left.mTotalTimeVisible, 0);
+
+ left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mLaunchCount, 2);
assertEquals(left.mTotalTimeInForeground, 100000);
+ assertEquals(left.mTotalTimeVisible, 100000 /*200000 - 100000*/);
- left.update("com.test.activity1", 250000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 250000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 250000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 250000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 150000);
+ assertEquals(left.mTotalTimeVisible, 150000 /*250000 - 100000*/);
- left.update("com.test.activity1", 300000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 300000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 250000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 300000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 150000);
+ assertEquals(left.mTotalTimeVisible, 200000 /*300000 - 100000*/);
- left.update("com.test.activity1", 350000, MOVE_TO_FOREGROUND);
+ left.update("com.test.activity1", 350000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mTotalTimeInForeground, 150000);
+ assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/);
- left.update("com.test.activity1", 400000, END_OF_DAY);
+ left.update("com.test.activity1", 400000, END_OF_DAY, 1);
assertEquals(left.mLastTimeUsed, 400000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(END_OF_DAY));
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mTotalTimeInForeground, 200000);
+ assertEquals(left.mTotalTimeVisible, 300000 /*400000 - 100000*/);
}
@Test
@@ -265,28 +320,41 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ left.update("com.test.activity2", 100000, Event.ACTIVITY_RESUMED, 2);
assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mActivities.get(2), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mLaunchCount, 2);
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/);
- left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity2", 450000, ACTIVITY_PAUSED, 2);
assertEquals(left.mLastTimeUsed, 450000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null);
+ assertEquals(left.mLastTimeVisible, 450000);
+ assertEquals(left.mActivities.get(2), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 + 100000 /*450000 - 350000*/);
- left.update(null, 500000, END_OF_DAY);
+ left.update("com.test.activity1", 550000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastTimeVisible, 550000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
assertEquals(left.mTotalTimeInForeground, 350000);
+ assertEquals(left.mTotalTimeVisible, 350000 + 100000 /*550000 - 450000*/);
+
+ left.update("com.test.activity2", 650000, ACTIVITY_STOPPED, 2);
+ assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastTimeVisible, 650000);
+ assertEquals(left.mActivities.get(2), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 350000);
+ assertEquals(left.mTotalTimeVisible, 450000 + 100000 /*650000 - 550000*/);
}
@Test
@@ -294,15 +362,14 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START);
+ left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 200000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(FOREGROUND_SERVICE_START));
- assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 200000);
}
@@ -311,15 +378,15 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.service1", 100000, CONTINUING_FOREGROUND_SERVICE);
+ left.update("com.test.service1", 100000,
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000);
}
@@ -329,16 +396,15 @@
left.mBeginTimeStamp = 100000;
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
- left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE);
+ left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
- new Integer(ROLLOVER_FOREGROUND_SERVICE));
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000);
}
@@ -348,27 +414,28 @@
left.mBeginTimeStamp = 100000;
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/);
- left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START);
+ left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(FOREGROUND_SERVICE_START));
assertEquals(left.mTotalTimeForegroundServiceUsed, 250000);
- left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
- assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 50000 /*500000 - 450000*/);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed,
+ 250000 + 50000 /*500000 - 450000*/);
}
@Test
@@ -377,27 +444,27 @@
left.mBeginTimeStamp = 100000;
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
left.update("com.test.service2", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"),
+ assertEquals(left.mForegroundServices.get("com.test.service2"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/);
- left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null);
- assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 100000 /*450000 - 350000*/);
+ assertEquals(left.mForegroundServices.get("com.test.service2"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed,
+ 250000 + 100000 /*450000 - 350000*/);
- left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE);
+ left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000);
}
@@ -407,76 +474,117 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ left.update("com.test.activity2", 100000, Event.ACTIVITY_RESUMED, 2);
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
left.update("com.test.service2", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeUsed, 100000);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mActivities.get(2), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"),
+ assertEquals(left.mForegroundServices.get("com.test.service2"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
+ assertEquals(left.mLaunchCount, 2);
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/);
- left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 400000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 /*400000 - 100000*/);
- left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity2", 450000, ACTIVITY_PAUSED, 2);
assertEquals(left.mLastTimeUsed, 450000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null);
+ assertEquals(left.mLastTimeVisible, 450000);
+ assertEquals(left.mActivities.get(2), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 + 100000 /*450000 - 350000*/);
- left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null);
- assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 + 100000 /*500000 - 400000*/);
+ assertEquals(left.mForegroundServices.get("com.test.service2"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed,
+ 300000 + 100000 /*500000 - 400000*/);
- left.update(null, 550000, END_OF_DAY);
+ left.update(null, 550000, END_OF_DAY, 0);
assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastTimeVisible, 550000);
assertEquals(left.mTotalTimeInForeground, 350000);
- left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE);
+ assertEquals(left.mTotalTimeVisible, 450000);
+
+ left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
assertEquals(left.mTotalTimeForegroundServiceUsed, 400000);
}
+ @Test
+ public void testEvent_FLUSH_TO_DISK() {
+ testClosingEvent(FLUSH_TO_DISK);
+ }
+
+ private void testClosingEvent(int eventType) {
+ // When these three closing events are received, all open activities/services need to be
+ // closed and usage stats are updated.
+ if (eventType != FLUSH_TO_DISK) {
+ fail("Closing eventType must be one of FLUSH_TO_DISK");
+ }
+
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+
+ left.update("com.test.service1", 150000, FOREGROUND_SERVICE_START, 0);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 150000);
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
+ new Integer(FOREGROUND_SERVICE_START));
+
+ left.update(null, 200000, eventType, 0);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 200000);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 200000 - 150000);
+ }
+
void compareUsageStats(UsageStats us1, UsageStats us2) {
assertEquals(us1.mPackageName, us2.mPackageName);
assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp);
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+ assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
- assertEquals(us1.mLastForegroundActivityEventMap.size(),
- us2.mLastForegroundActivityEventMap.size());
- for (int i = 0; i < us1.mLastForegroundActivityEventMap.size(); i++) {
- assertEquals(us1.mLastForegroundActivityEventMap.keyAt(i),
- us2.mLastForegroundActivityEventMap.keyAt(i));
- assertEquals(us1.mLastForegroundActivityEventMap.valueAt(i),
- us2.mLastForegroundActivityEventMap.valueAt(i));
+ assertEquals(us1.mActivities.size(),
+ us2.mActivities.size());
+ for (int i = 0; i < us1.mActivities.size(); i++) {
+ assertEquals(us1.mActivities.keyAt(i),
+ us2.mActivities.keyAt(i));
+ assertEquals(us1.mActivities.valueAt(i),
+ us2.mActivities.valueAt(i));
}
- assertEquals(us1.mLastForegroundServiceEventMap.size(),
- us2.mLastForegroundServiceEventMap.size());
- for (int i = 0; i < us1.mLastForegroundServiceEventMap.size(); i++) {
- assertEquals(us1.mLastForegroundServiceEventMap.keyAt(i),
- us2.mLastForegroundServiceEventMap.keyAt(i));
- assertEquals(us1.mLastForegroundServiceEventMap.valueAt(i),
- us2.mLastForegroundServiceEventMap.valueAt(i));
+ assertEquals(us1.mForegroundServices.size(),
+ us2.mForegroundServices.size());
+ for (int i = 0; i < us1.mForegroundServices.size(); i++) {
+ assertEquals(us1.mForegroundServices.keyAt(i),
+ us2.mForegroundServices.keyAt(i));
+ assertEquals(us1.mForegroundServices.valueAt(i),
+ us2.mForegroundServices.valueAt(i));
}
assertEquals(us1.mChooserCounts, us2.mChooserCounts);
}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceService.java b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
index ac8d7ab..3bca5fb 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceService.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
@@ -25,9 +25,25 @@
public class BinderWorkSourceService extends Service {
private final IBinderWorkSourceService.Stub mBinder =
new IBinderWorkSourceService.Stub() {
+ public int getBinderCallingUid() {
+ return Binder.getCallingUid();
+ }
+
public int getIncomingWorkSourceUid() {
return Binder.getCallingWorkSourceUid();
}
+
+ public int getThreadLocalWorkSourceUid() {
+ return ThreadLocalWorkSource.getUid();
+ }
+
+ public void setWorkSourceProvider(int uid) {
+ Binder.setWorkSourceProvider(() -> uid);
+ }
+
+ public void clearWorkSourceProvider() {
+ Binder.setWorkSourceProvider(Binder::getCallingUid);
+ }
};
public IBinder onBind(Intent intent) {
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index ec17803..d1dbd3c 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -163,4 +163,24 @@
// Initial work source restored.
assertEquals(UID, Binder.getCallingWorkSourceUid());
}
+
+ @Test
+ public void workSourceProvider_default() throws Exception {
+ Binder.clearCallingWorkSource();
+ mService.clearWorkSourceProvider();
+ assertEquals(Process.myUid(), mService.getThreadLocalWorkSourceUid());
+ }
+
+ @Test
+ public void workSourceProvider_customProvider() throws Exception {
+ Binder.clearCallingWorkSource();
+ mService.clearWorkSourceProvider();
+ // Calling uid should not be used.
+ mService.setWorkSourceProvider(SECOND_UID);
+ try {
+ assertEquals(SECOND_UID, mService.getThreadLocalWorkSourceUid());
+ } finally {
+ mService.clearWorkSourceProvider();
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
index 05d4e82..9322400 100644
--- a/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
@@ -18,4 +18,8 @@
interface IBinderWorkSourceService {
int getIncomingWorkSourceUid();
+ int getBinderCallingUid();
+ int getThreadLocalWorkSourceUid();
+ void setWorkSourceProvider(int uid);
+ void clearWorkSourceProvider();
}
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index da17b56..a828f44 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -16,13 +16,32 @@
package android.os;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.After;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
public class PowerManagerTest extends AndroidTestCase {
private PowerManager mPm;
+ private UiDevice mUiDevice;
+ private Executor mExec = Executors.newSingleThreadExecutor();
+ @Mock
+ private PowerManager.ThermalStatusCallback mCallback;
+ private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
/**
* Setup any common data for the upcoming tests.
@@ -30,7 +49,18 @@
@Override
public void setUp() throws Exception {
super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mUiDevice.executeShellCommand("cmd thermalservice override-status 0");
+ }
+
+ /**
+ * Reset data for the upcoming tests.
+ */
+ @After
+ public void tearDown() throws Exception {
+ mUiDevice.executeShellCommand("cmd thermalservice reset");
}
/**
@@ -137,4 +167,35 @@
// TODO: Threaded test (needs handler) to make sure timed wakelocks work too
}
+
+ @Test
+ public void testGetThermalStatus() throws Exception {
+ int status = 0;
+ assertEquals(status, mPm.getCurrentThermalStatus());
+ status = 3;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + Integer.toString(status));
+ assertEquals(status, mPm.getCurrentThermalStatus());
+ }
+
+ @Test
+ public void testThermalStatusCallback() throws Exception {
+ mPm.registerThermalStatusCallback(mCallback, mExec);
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(0);
+ reset(mCallback);
+ int status = 3;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + Integer.toString(status));
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ reset(mCallback);
+ mPm.unregisterThermalStatusCallback(mCallback);
+ status = 2;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + Integer.toString(status));
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(status);
+
+ }
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a8f9e8a..3a37fb6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -183,6 +183,7 @@
Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE,
Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
+ Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED,
Settings.Global.CONVERSATION_ACTIONS_UPDATE_CONTENT_URL,
Settings.Global.CONVERSATION_ACTIONS_UPDATE_METADATA_URL,
Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
@@ -247,6 +248,7 @@
Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
@@ -413,6 +415,7 @@
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
Settings.Global.SHOW_TEMPERATURE_WARNING,
+ Settings.Global.SIGNED_CONFIG_VERSION,
Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED,
@@ -468,7 +471,9 @@
Settings.Global.GPU_DEBUG_APP,
Settings.Global.GPU_DEBUG_LAYERS,
Settings.Global.GPU_DEBUG_LAYERS_GLES,
- Settings.Global.ANGLE_ENABLED_APP,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
Settings.Global.GPU_DEBUG_LAYER_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
@@ -477,6 +482,7 @@
Settings.Global.USER_SWITCHER_ENABLED,
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
+ Settings.Global.USB_ALARM_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
Settings.Global.WEBVIEW_MULTIPROCESS,
@@ -536,7 +542,8 @@
Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION,
Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
- Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+ Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
+ Settings.Global.BACKUP_MULTI_USER_ENABLED);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index 108585d..04e8802 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -16,6 +16,10 @@
package android.provider;
+import static org.hamcrest.Matchers.aMapWithSize;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertThat;
+
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -26,6 +30,7 @@
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
@@ -33,10 +38,19 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Suppress;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/** Unit test for SettingsProvider. */
public class SettingsProviderTest extends AndroidTestCase {
+ /**
+ * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+ * API.
+ */
+ private static final Uri CONFIG_CONTENT_URI =
+ Uri.parse("content://" + Settings.AUTHORITY + "/config");
+
@MediumTest
public void testNameValueCache() {
ContentResolver r = getContext().getContentResolver();
@@ -379,4 +393,109 @@
assertTrue(ssaid.equals(ssaid2));
}
+
+ @MediumTest
+ public void testCall_putAndGetConfig() {
+ ContentResolver r = getContext().getContentResolver();
+ String name = "key1";
+ String value = "value1";
+ String newValue = "value2";
+ Bundle args = new Bundle();
+ args.putString(Settings.NameValueTable.VALUE, value);
+
+ try {
+ // value is empty
+ Bundle results =
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+ assertNull(results.get(Settings.NameValueTable.VALUE));
+
+ // save value
+ results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+ assertNull(results);
+
+ // value is no longer empty
+ results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+ assertEquals(value, results.get(Settings.NameValueTable.VALUE));
+
+ // save new value
+ args.putString(Settings.NameValueTable.VALUE, newValue);
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+
+ // new value is returned
+ results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+ assertEquals(newValue, results.get(Settings.NameValueTable.VALUE));
+ } finally {
+ // clean up
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+ }
+ }
+
+ @MediumTest
+ public void testCall_deleteConfig() {
+ ContentResolver r = getContext().getContentResolver();
+ String name = "key1";
+ String value = "value1";
+ Bundle args = new Bundle();
+ args.putString(Settings.NameValueTable.VALUE, value);
+
+ try {
+ // save value
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+
+ // get value
+ Bundle results =
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+ assertEquals(value, results.get(Settings.NameValueTable.VALUE));
+
+ // delete value
+ results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+
+ // value is empty now
+ results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+ assertNull(results.get(Settings.NameValueTable.VALUE));
+ } finally {
+ // clean up
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+ }
+ }
+
+ @MediumTest
+ public void testCall_listConfig() {
+ ContentResolver r = getContext().getContentResolver();
+ String prefix = "foo";
+ String newPrefix = "bar";
+ String name = prefix + "/" + "key1";
+ String newName = newPrefix + "/" + "key1";
+ String value = "value1";
+ String newValue = "value2";
+ Bundle args = new Bundle();
+ args.putString(Settings.NameValueTable.VALUE, value);
+
+ try {
+ // save both values
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+ args.putString(Settings.NameValueTable.VALUE, newValue);
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, newName, args);
+
+ // list all values
+ Bundle result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG,
+ null, null);
+ Map<String, String> keyValueMap =
+ (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
+ assertThat(keyValueMap.size(), greaterThanOrEqualTo(2));
+ assertEquals(value, keyValueMap.get(name));
+ assertEquals(newValue, keyValueMap.get(newName));
+
+ // list values for prefix
+ args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
+ result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG, null, args);
+ keyValueMap = (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
+ assertThat(keyValueMap, aMapWithSize(1));
+ assertEquals(value, keyValueMap.get(name));
+ } finally {
+ // clean up
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+ r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, newName, null);
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
index 8d6fbd5..61ab152 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -28,7 +28,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO(b/73862682): Add tests for RecoveryCertPath
+import java.security.cert.CertPath;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class KeyChainSnapshotTest {
@@ -43,35 +44,41 @@
private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
private static final String KEY_ALIAS = "steph";
private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 };
+ private static final CertPath CERT_PATH = TestData.getThmCertPath();
@Test
- public void build_setsCounterId() {
+ public void build_setsCounterId() throws Exception {
assertEquals(COUNTER_ID, createKeyChainSnapshot().getCounterId());
}
@Test
- public void build_setsSnapshotVersion() {
+ public void build_setsSnapshotVersion() throws Exception {
assertEquals(SNAPSHOT_VERSION, createKeyChainSnapshot().getSnapshotVersion());
}
@Test
- public void build_setsMaxAttempts() {
+ public void build_setsMaxAttempts() throws Exception {
assertEquals(MAX_ATTEMPTS, createKeyChainSnapshot().getMaxAttempts());
}
@Test
- public void build_setsServerParams() {
+ public void build_setsServerParams() throws Exception {
assertArrayEquals(SERVER_PARAMS, createKeyChainSnapshot().getServerParams());
}
@Test
- public void build_setsRecoveryKeyBlob() {
+ public void build_setsRecoveryKeyBlob() throws Exception {
assertArrayEquals(RECOVERY_KEY_BLOB,
createKeyChainSnapshot().getEncryptedRecoveryKeyBlob());
}
@Test
- public void build_setsKeyChainProtectionParams() {
+ public void build_setsCertPath() throws Exception {
+ assertEquals(CERT_PATH, createKeyChainSnapshot().getTrustedHardwareCertPath());
+ }
+
+ @Test
+ public void build_setsKeyChainProtectionParams() throws Exception {
KeyChainSnapshot snapshot = createKeyChainSnapshot();
assertEquals(1, snapshot.getKeyChainProtectionParams().size());
@@ -85,7 +92,7 @@
}
@Test
- public void build_setsWrappedApplicationKeys() {
+ public void build_setsWrappedApplicationKeys() throws Exception {
KeyChainSnapshot snapshot = createKeyChainSnapshot();
assertEquals(1, snapshot.getWrappedApplicationKeys().size());
@@ -95,42 +102,49 @@
}
@Test
- public void writeToParcel_writesCounterId() {
+ public void writeToParcel_writesCounterId() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(COUNTER_ID, snapshot.getCounterId());
}
@Test
- public void writeToParcel_writesSnapshotVersion() {
+ public void writeToParcel_writesSnapshotVersion() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(SNAPSHOT_VERSION, snapshot.getSnapshotVersion());
}
@Test
- public void writeToParcel_writesMaxAttempts() {
+ public void writeToParcel_writesMaxAttempts() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(MAX_ATTEMPTS, snapshot.getMaxAttempts());
}
@Test
- public void writeToParcel_writesServerParams() {
+ public void writeToParcel_writesServerParams() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertArrayEquals(SERVER_PARAMS, snapshot.getServerParams());
}
@Test
- public void writeToParcel_writesKeyRecoveryBlob() {
+ public void writeToParcel_writesKeyRecoveryBlob() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertArrayEquals(RECOVERY_KEY_BLOB, snapshot.getEncryptedRecoveryKeyBlob());
}
@Test
- public void writeToParcel_writesKeyChainProtectionParams() {
+ public void writeToParcel_writesCertPath() throws Exception {
+ KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+ assertEquals(CERT_PATH, snapshot.getTrustedHardwareCertPath());
+ }
+
+ @Test
+ public void writeToParcel_writesKeyChainProtectionParams() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(1, snapshot.getKeyChainProtectionParams().size());
@@ -144,7 +158,7 @@
}
@Test
- public void writeToParcel_writesWrappedApplicationKeys() {
+ public void writeToParcel_writesWrappedApplicationKeys() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(1, snapshot.getWrappedApplicationKeys().size());
@@ -153,7 +167,7 @@
assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
}
- private static KeyChainSnapshot createKeyChainSnapshot() {
+ private static KeyChainSnapshot createKeyChainSnapshot() throws Exception {
return new KeyChainSnapshot.Builder()
.setCounterId(COUNTER_ID)
.setSnapshotVersion(SNAPSHOT_VERSION)
@@ -162,6 +176,7 @@
.setEncryptedRecoveryKeyBlob(RECOVERY_KEY_BLOB)
.setKeyChainProtectionParams(Lists.newArrayList(createKeyChainProtectionParams()))
.setWrappedApplicationKeys(Lists.newArrayList(createWrappedApplicationKey()))
+ .setTrustedHardwareCertPath(CERT_PATH)
.build();
}
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
new file mode 100644
index 0000000..dd8cd8d
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.cert.CertificateException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RecoveryCertPathTest {
+
+ @Test
+ public void createRecoveryCertPath_getCertPath_succeeds() throws Exception {
+ RecoveryCertPath recoveryCertPath = RecoveryCertPath.createRecoveryCertPath(
+ TestData.getThmCertPath());
+ assertEquals(TestData.getThmCertPath(), recoveryCertPath.getCertPath());
+ }
+
+ @Test
+ public void getCertPath_throwsIfCannnotDecode() {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeByteArray(new byte[]{0, 1, 2, 3});
+ parcel.setDataPosition(0);
+ RecoveryCertPath recoveryCertPath = RecoveryCertPath.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ try {
+ recoveryCertPath.getCertPath();
+ fail("Did not throw when attempting to decode invalid cert path");
+ } catch (CertificateException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void writeToParcel_writesCertPath() throws Exception {
+ RecoveryCertPath recoveryCertPath =
+ writeToThenReadFromParcel(
+ RecoveryCertPath.createRecoveryCertPath(TestData.getThmCertPath()));
+ assertEquals(TestData.getThmCertPath(), recoveryCertPath.getCertPath());
+ }
+
+ private RecoveryCertPath writeToThenReadFromParcel(RecoveryCertPath recoveryCertPath) {
+ Parcel parcel = Parcel.obtain();
+ recoveryCertPath.writeToParcel(parcel, /*flags=*/ 0);
+ parcel.setDataPosition(0);
+ RecoveryCertPath fromParcel = RecoveryCertPath.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return fromParcel;
+ }
+}
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/TestData.java b/core/tests/coretests/src/android/security/keystore/recovery/TestData.java
new file mode 100644
index 0000000..829a92a
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TestData.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.util.Base64;
+
+/** This class provides data for testing purposes. */
+class TestData {
+
+ private static final String THM_CERT_PATH_BASE64 = ""
+ + "MIIIXTCCBRowggMCoAMCAQICEB35ZwzVpI9ssXg9SAehnU0wDQYJKoZIhvcNAQEL"
+ + "BQAwMTEvMC0GA1UEAxMmR29vZ2xlIENsb3VkIEtleSBWYXVsdCBTZXJ2aWNlIFJv"
+ + "b3QgQ0EwHhcNMTgwNTA3MTg1ODEwWhcNMjgwNTA4MTg1ODEwWjA5MTcwNQYDVQQD"
+ + "Ey5Hb29nbGUgQ2xvdWQgS2V5IFZhdWx0IFNlcnZpY2UgSW50ZXJtZWRpYXRlIENB"
+ + "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA73TrvH3j6zEimpcc32tx"
+ + "2iupWwfyzdE5l4Ejc5EBYzx0aZH6b/KDuutwustk0IoyjlGySMBz/21YgWejIm+n"
+ + "duAlpk7WY5kYHp0XWtzdmxZknmWTqugPeNZeiKEjoDmpyIbY6N+f13hQ2RVh+WDT"
+ + "EowQ/i04WBL75chshlIG+3A42g5Qr7DZEKdT9oJQqkntzj0cGyJ5X8BwjeTiJrvY"
+ + "k2Kn/0555/Kpp65G3Rf29VPPU3i67kthAT3SavLBpH03S4WZ+QlfrAiGQziydtz9"
+ + "t7mSk1xefjax5ZWAuJAfCbKfI3VWAcaUr4P57BzmDcSi0jgs1aM3t2BrPfAMRxWv"
+ + "35yDZnrC+HipzkjyDGBfHmFgoglyhc9e/Kj3mSusO0Rq1wguVXKs2hKXRoaGJuHt"
+ + "e3YIwTC1pLznqvolhD1nPoXf8rMzgHRzlc9H8iXsgB1p7975nh5WCPrMDX2eAmYd"
+ + "a0xTMccTeBzIM2ohxQsxlh5rsjXVNU3ihbWkHquzIiwFcAtldP3dMksj0dn/DnYD"
+ + "yokjEgU/z2I216E93x9hmKkEk6Pp7o8t/z6lwMT9FJIuzp7NREnWCSi+e5s2E7FD"
+ + "j6S7xY2zEIUHrmwuuJc0jzJnwdZ+0myinaTmBDvBXR5cU1cmEAZoheCAoRv9Z/6o"
+ + "ASczLF0C4uuVfA5GXcAm14cCAwEAAaMmMCQwDgYDVR0PAQH/BAQDAgGGMBIGA1Ud"
+ + "EwEB/wQIMAYBAf8CAQEwDQYJKoZIhvcNAQELBQADggIBAEPht79yQm8woQbPB1Bs"
+ + "eotkzJtTWTO9fnIWwNiRfQ3vJFXf69ghE77wUS13Ez3FlgNPj0Qxmg5ouE0d2yYV"
+ + "4AUrXnEGZELcyN2XHRXyNK0zXgnr3x6eZyY7QfgGKJgkyja5TS6ZPWGyaLKhClS0"
+ + "AYZSzWJtz0+AkGCdTbmyy7ShdXJ+GfnmssbndZA62VhcjeQmHsDq7V3PKAsp4/B9"
+ + "PzcnTrgkUFNnP1F1pr7JpUUX3xyRFy6gjIrUx1fcOFRxFYPWGLLMZ6P41rafm+M/"
+ + "CbBNr5CY7NrZjr34jLqWycfYes49o9OK44X/wPrxj0Sjg+VrW21+AJ9vrM7DS5hE"
+ + "QX1lDbDtQGkk3N1vgCTo6xt9LXsEu4xUT5bk7YAfpJqM0ltDFPwYAGCbjSkVT/M5"
+ + "JVZkKiUW668Us67x8yZc/5bxbvTA+5xrYhak/VYIBY6qub4J+bKwadw6uBgxnq4P"
+ + "hwgwjfaoJy9YAXCswjCtaE9GwkVmRnJE9vFjJ33IGf37hFTYEHBFy4FomVmQwRFZ"
+ + "TIe7tkKDq9i18F7lzBPJPO6wEG8bxi4csatrjcVHR9erpY5u6ebtkKG8qsan9qzh"
+ + "iWAgSytiT++HejZeoQ+RRgQWjupjdDo5/0oSdQqvaN8Ah6C2J+ecCZ12Lu0FwF+t"
+ + "t9Ie3pF6W8TzxzuMdFWq+afvMIIDOzCCASOgAwIBAgIRAOTj/iNQb6/Qit7zAW9n"
+ + "cL0wDQYJKoZIhvcNAQELBQAwOTE3MDUGA1UEAxMuR29vZ2xlIENsb3VkIEtleSBW"
+ + "YXVsdCBTZXJ2aWNlIEludGVybWVkaWF0ZSBDQTAeFw0xODA1MDcyMjE4MTFaFw0y"
+ + "MzA1MDgyMjE4MTFaMDIxMDAuBgNVBAMTJ0dvb2dsZSBDbG91ZCBLZXkgVmF1bHQg"
+ + "U2VydmljZSBFbmRwb2ludDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI4MEUp5"
+ + "IHwATNfpBuJYIUX6JMsHZt798YO0JlWYy6nVVa1lxf9c+xxONJh+T5aio370RlIE"
+ + "uiq5R7vCHt0VGsCjEDAOMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB"
+ + "AGf6QU58lU+gGzy8hnp0suR/ixC++CExtf39pDHkdfU/e3ui4ROR+pjQ5F7okDFW"
+ + "eKSCNcJZ7tyXMJ9g7/I0qVY8Bj/gRnlVokdl/wD5PiL9GIzqWfnHNe3T+xrAAAgO"
+ + "D0bEmjgwNYmekfUIYQczd04d7ZMGnmAkpVH/0O2mf9q5x9fMlbKuAygUqQ/gmnlg"
+ + "xKfl9DSRWi4oMBOqlKlCRP1XAh3anu92+M/EhsFbyc07CWZY0SByX5M/cHVMLhUX"
+ + "jZHvcYLyOmJWJmXznidgyNeIR6t9yDB55iCt7WSn3qMY+9vA9ELzt8jYpBNaKc0G"
+ + "bWQkRzYWegkf4kMis98eQ3SnAKbRz6669nmuAdxKs9/LK6BlFOFw1xvsTRQ96dBa"
+ + "oiX2XGhou+Im0Td/AMs0Aigz2N+Ujq/yW//35GZQfdGGIYtFbkcltStygjIJyAM1"
+ + "pBhyBBkJhOhRpO4fXh98aq8H5J7R9i5A9WpnDstAxPxcNCDWn0O/WxhPvVZkFTpi"
+ + "NXh9dnlJ/kZe+j+z5ZMaxW435drLPx2AQKjXA9GgGrFPltTUyGycmEGtuxLvSnm/"
+ + "zPlmk5FUk7x2wEr0+bZ3cx0JHHgAtgXpe0jkDi8Bw8O3X7mUOjxVhYU6auiYJezW"
+ + "9LGmweaKwYvS04UCWOReolUVexob9LI/VX1JrrwD3s7k";
+
+ static CertPath getThmCertPath() {
+ try {
+ return decodeCertPath(THM_CERT_PATH_BASE64);
+ } catch (Exception e) {
+ // Should never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static CertPath decodeCertPath(String base64CertPath) throws Exception {
+ byte[] certPathBytes = Base64.getDecoder().decode(base64CertPath);
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return certFactory.generateCertPath(new ByteArrayInputStream(certPathBytes), "PkiPath");
+ }
+}
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 3d15eb9..a0dca2c 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -133,7 +133,8 @@
public void buildForStaticLayout() {
MeasuredParagraph mt = null;
- mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, false, false, null);
+ mt = MeasuredParagraph.buildForStaticLayout(
+ PAINT, "XXX", 0, 3, LTR, false, false, null /* no hint */, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -147,8 +148,8 @@
assertNotNull(mt.getMeasuredText());
// Recycle it
- MeasuredParagraph mt2 =
- MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, false, false, mt);
+ MeasuredParagraph mt2 = MeasuredParagraph.buildForStaticLayout(
+ PAINT, "_VVV_", 1, 4, RTL, false, false, null /* no hint */, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ed80cd7..2ad6028 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -19,6 +19,7 @@
import static android.view.InsetsState.TYPE_TOP_BAR;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
+import static org.mockito.Mockito.mock;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
@@ -33,7 +34,7 @@
@RunWith(AndroidJUnit4.class)
public class InsetsControllerTest {
- private InsetsController mController = new InsetsController();
+ private InsetsController mController = new InsetsController(mock(ViewRootImpl.class));
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mLeash;
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 5a20ba2..6d0f984 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -53,7 +53,7 @@
.setName("testSurface")
.build();
mConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, new InsetsState(),
- () -> mMockTransaction);
+ () -> mMockTransaction, mMockController);
mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
index f0faaf6..4a6c093 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -16,6 +16,9 @@
package android.view.textclassifier;
+import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_LOCAL;
+import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_REMOTE;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.Person;
@@ -27,16 +30,26 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Locale;
+import java.util.function.Function;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ActionsSuggestionsHelperTest {
+ private static final String LOCALE_TAG = Locale.US.toLanguageTag();
+ private static final Function<CharSequence, String> LANGUAGE_DETECTOR =
+ charSequence -> LOCALE_TAG;
+
@Test
public void testToNativeMessages_emptyInput() {
ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
+ ActionsSuggestionsHelper.toNativeMessages(
+ Collections.emptyList(), LANGUAGE_DETECTOR);
assertThat(conversationMessages).isEmpty();
}
@@ -44,114 +57,89 @@
@Test
public void testToNativeMessages_noTextMessages() {
ConversationActions.Message messageWithoutText =
- new ConversationActions.Message.Builder().build();
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE).build();
ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
ActionsSuggestionsHelper.toNativeMessages(
- Collections.singletonList(messageWithoutText));
+ Collections.singletonList(messageWithoutText), LANGUAGE_DETECTOR);
assertThat(conversationMessages).isEmpty();
}
@Test
- public void testToNativeMessages_missingPersonInFirstMessage() {
- ConversationActions.Message firstMessage =
- new ConversationActions.Message.Builder()
- .setText("first")
- .build();
- ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder()
- .setText("second")
- .setAuthor(new Person.Builder().build())
- .build();
- ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder()
- .setText("third")
- .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
- .build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage));
-
- assertThat(conversationMessages).hasLength(2);
- assertNativeMessage(conversationMessages[0], secondMessage.getText(), 1);
- assertNativeMessage(conversationMessages[1], thirdMessage.getText(), 0);
- }
-
- @Test
- public void testToNativeMessages_missingPersonInMiddleOfConversation() {
- ConversationActions.Message firstMessage =
- new ConversationActions.Message.Builder()
- .setText("first")
- .setAuthor(new Person.Builder().setName("first").build())
- .build();
- ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder()
- .setText("second")
- .build();
- ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder()
- .setText("third")
- .setAuthor(new Person.Builder().setName("third").build())
- .build();
- ConversationActions.Message fourthMessage =
- new ConversationActions.Message.Builder()
- .setText("fourth")
- .setAuthor(new Person.Builder().setName("fourth").build())
- .build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
-
- assertThat(conversationMessages).hasLength(2);
- assertNativeMessage(conversationMessages[0], thirdMessage.getText(), 2);
- assertNativeMessage(conversationMessages[1], fourthMessage.getText(), 1);
- }
-
- @Test
public void testToNativeMessages_userIdEncoding() {
Person userA = new Person.Builder().setName("userA").build();
Person userB = new Person.Builder().setName("userB").build();
ConversationActions.Message firstMessage =
- new ConversationActions.Message.Builder()
+ new ConversationActions.Message.Builder(userB)
.setText("first")
- .setAuthor(userB)
.build();
ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder()
+ new ConversationActions.Message.Builder(userA)
.setText("second")
- .setAuthor(userA)
.build();
ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder()
+ new ConversationActions.Message.Builder(PERSON_USER_LOCAL)
.setText("third")
- .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
.build();
ConversationActions.Message fourthMessage =
- new ConversationActions.Message.Builder()
+ new ConversationActions.Message.Builder(userA)
.setText("fourth")
- .setAuthor(userA)
.build();
ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+ Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage),
+ LANGUAGE_DETECTOR);
assertThat(conversationMessages).hasLength(4);
- assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2);
- assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1);
- assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0);
- assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1);
+ assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2, 0);
+ assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
+ assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0, 0);
+ assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1, 0);
+ }
+
+ @Test
+ public void testToNativeMessages_referenceTime() {
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
+ .setText("first")
+ .setReferenceTime(createZonedDateTimeFromMsUtc(1000))
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
+ .setText("second")
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
+ .setText("third")
+ .setReferenceTime(createZonedDateTimeFromMsUtc(2000))
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage),
+ LANGUAGE_DETECTOR);
+
+ assertThat(conversationMessages).hasLength(3);
+ assertNativeMessage(conversationMessages[0], firstMessage.getText(), 1, 1000);
+ assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
+ assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 1, 2000);
+ }
+
+ private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneId.of("UTC"));
}
private static void assertNativeMessage(
ActionsSuggestionsModel.ConversationMessage nativeMessage,
CharSequence text,
- int userId) {
+ int userId,
+ long referenceTimeInMsUtc) {
assertThat(nativeMessage.getText()).isEqualTo(text.toString());
assertThat(nativeMessage.getUserId()).isEqualTo(userId);
+ assertThat(nativeMessage.getLocales()).isEqualTo(LOCALE_TAG);
+ assertThat(nativeMessage.getReferenceTimeMsUtc()).isEqualTo(referenceTimeInMsUtc);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
index bae2be3..aaadefb 100644
--- a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -56,5 +56,7 @@
Intent intent = labeledIntent.getIntent();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+ assertThat(
+ intent.getBooleanExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, false)).isTrue();
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 91a5440..aaf7312 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -189,6 +189,7 @@
Instant.ofEpochMilli(946771200000L), // 2000-01-02
ZoneId.of("UTC"));
final String text = "text";
+ final String packageName = "packageName";
final TextClassification.Request reference =
new TextClassification.Request.Builder(text, 0, text.length())
@@ -196,6 +197,7 @@
.setReferenceTime(referenceTime)
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -204,12 +206,13 @@
final TextClassification.Request result =
TextClassification.Request.CREATOR.createFromParcel(parcel);
- assertEquals(text, result.getText());
+ assertEquals(text, result.getText().toString());
assertEquals(0, result.getStartIndex());
assertEquals(text.length(), result.getEndIndex());
assertEquals(referenceTime, result.getReferenceTime());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(referenceTime, result.getReferenceTime());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index fbcb629..9b5c034 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -373,7 +373,10 @@
public void testSuggestConversationActions_textReplyOnly_maxThree() {
if (isTextClassifierDisabled()) return;
ConversationActions.Message message =
- new ConversationActions.Message.Builder().setText("Hello").build();
+ new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_REMOTE)
+ .setText("Hello")
+ .build();
ConversationActions.TypeConfig typeConfig =
new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
.setIncludedTypes(
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 75ca769..1dcaed6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -69,10 +69,12 @@
final String bundleKey = "experiment.str";
final Bundle bundle = new Bundle();
bundle.putString(bundleKey, "bundle");
+ final String packageName = "packageName";
final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
.setExtras(bundle)
.build();
+ reference.setCallingPackageName(packageName);
final Parcel parcel = Parcel.obtain();
reference.writeToParcel(parcel, 0);
@@ -81,5 +83,6 @@
assertEquals(text, result.getText());
assertEquals("bundle", result.getExtras().getString(bundleKey));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index f6ec0e6..f022d04 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -64,7 +64,7 @@
public void testParcel() {
final String fullText = "this is just a test";
final TextLinks reference = new TextLinks.Builder(fullText)
- .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f))
+ .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f), BUNDLE)
.addLink(5, 12, getEntityScores(.8f, .1f, .5f))
.setExtras(BUNDLE)
.build();
@@ -82,6 +82,7 @@
assertEquals(1, resultList.get(0).getEntityCount());
assertEquals(TextClassifier.TYPE_OTHER, resultList.get(0).getEntity(0));
assertEquals(1.f, resultList.get(0).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
+ assertEquals(BUNDLE_VALUE, resultList.get(0).getExtras().getString(BUNDLE_KEY));
assertEquals(5, resultList.get(1).getStart());
assertEquals(12, resultList.get(1).getEnd());
assertEquals(3, resultList.get(1).getEntityCount());
@@ -96,6 +97,7 @@
@Test
public void testParcelOptions() {
+ final String packageName = "packageName";
final TextClassifier.EntityConfig entityConfig = TextClassifier.EntityConfig.create(
Arrays.asList(TextClassifier.HINT_TEXT_IS_EDITABLE),
Arrays.asList("a", "b", "c"),
@@ -105,6 +107,7 @@
.setEntityConfig(entityConfig)
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -119,5 +122,6 @@
assertEquals(new HashSet<String>(Arrays.asList("a", "c")),
result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 7ea5108b..2ea49f7 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -75,11 +75,13 @@
@Test
public void testParcelRequest() {
final String text = "text";
+ final String packageName = "packageName";
final TextSelection.Request reference =
new TextSelection.Request.Builder(text, 0, text.length())
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -87,10 +89,11 @@
parcel.setDataPosition(0);
final TextSelection.Request result = TextSelection.Request.CREATOR.createFromParcel(parcel);
- assertEquals(text, result.getText());
+ assertEquals(text, result.getText().toString());
assertEquals(0, result.getStartIndex());
assertEquals(text.length(), result.getEndIndex());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 8691e73..97f02cb 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -17,6 +17,7 @@
package com.android.internal.os;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
@@ -56,9 +57,9 @@
bcs.setSamplingInterval(5);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -72,17 +73,17 @@
assertEquals(1, callStatsList.get(0).transactionCode);
// CPU usage is sampled, should not be tracked here.
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 20;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(2, uidEntry.callCount);
assertEquals(1, uidEntry.recordedCallCount);
assertEquals(10, uidEntry.cpuTimeMicros);
assertEquals(1, callStatsList.size());
- callSession = bcs.callStarted(binder, 2);
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
assertEquals(3, uidEntry.callCount);
assertEquals(1, uidEntry.recordedCallCount);
@@ -97,9 +98,9 @@
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -115,9 +116,9 @@
assertEquals(binder.getClass(), callStatsList.get(0).binderClass);
assertEquals(1, callStatsList.get(0).transactionCode);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 20;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
assertEquals(2, uidEntry.callCount);
@@ -125,9 +126,9 @@
callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(1, callStatsList.size());
- callSession = bcs.callStarted(binder, 2);
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
assertEquals(3, uidEntry.callCount);
@@ -141,7 +142,7 @@
public void testEnableInBetweenCall() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
Binder binder = new Binder();
- bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(0, uidEntries.size());
@@ -152,7 +153,7 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
Binder binder = new Binder();
bcs.callThrewException(null, new IllegalStateException());
- bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(0, uidEntries.size());
@@ -165,17 +166,17 @@
bcs.setSamplingInterval(2);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 1000; // shoud be ignored.
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -202,13 +203,13 @@
bcs.setSamplingInterval(2);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 2 /* another method */);
+ callSession = bcs.callStarted(binder, 2 /* another method */, WORKSOURCE_UID);
bcs.time += 1000; // shoud be ignored.
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
@@ -245,9 +246,9 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new BinderWithGetTransactionName();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList =
bcs.getExportedCallStats();
@@ -260,18 +261,18 @@
bcs.setDetailedTracking(true);
Binder binder = new AnotherBinderWithGetTransactionName();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
Binder binder2 = new BinderWithGetTransactionName();
- callSession = bcs.callStarted(binder2, 1);
+ callSession = bcs.callStarted(binder2, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 2);
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList =
bcs.getExportedCallStats();
@@ -291,9 +292,9 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList =
bcs.getExportedCallStats();
@@ -305,9 +306,9 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList =
new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
@@ -321,13 +322,13 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList =
new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
@@ -340,13 +341,13 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.elapsedTime += 5;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.elapsedTime += 1;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList =
new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
@@ -367,17 +368,17 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new IllegalStateException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new IllegalStateException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new RuntimeException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
ArrayMap<String, Integer> expected = new ArrayMap<>();
expected.put("java.lang.IllegalStateException", 2);
@@ -387,13 +388,12 @@
@Test
public void testNoDataCollectedBeforeInitialDeviceStateSet() {
- TestBinderCallsStats bcs = new TestBinderCallsStats();
- bcs.setDeviceState(null);
+ TestBinderCallsStats bcs = new TestBinderCallsStats(null);
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
bcs.setDeviceState(mDeviceState.getReadonlyClient());
@@ -408,8 +408,8 @@
mDeviceState.setCharging(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(0, bcs.getUidEntries().size());
}
@@ -420,8 +420,8 @@
bcs.setDetailedTracking(true);
mDeviceState.setScreenInteractive(false);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -437,8 +437,8 @@
bcs.setDetailedTracking(true);
mDeviceState.setScreenInteractive(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -455,8 +455,8 @@
mDeviceState.setCharging(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(0, bcs.getExportedCallStats().size());
}
@@ -468,8 +468,8 @@
mDeviceState.setCharging(false);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(1, bcs.getExportedCallStats().size());
}
@@ -479,12 +479,12 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new IllegalStateException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
PrintWriter pw = new PrintWriter(new StringWriter());
- bcs.dump(pw, new HashMap<>(), true);
+ bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), true);
}
@Test
@@ -492,8 +492,8 @@
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(false);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(0, bcs.getExportedCallStats().size());
}
@@ -504,10 +504,10 @@
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
bcs.elapsedTime += 20;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(1, bcs.getExportedCallStats().size());
BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
@@ -528,22 +528,6 @@
}
@Test
- public void testCallingUidUsedWhenWorkSourceNotSet() {
- TestBinderCallsStats bcs = new TestBinderCallsStats();
- bcs.setDetailedTracking(true);
- bcs.workSourceUid = -1;
-
- Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
- assertEquals(1, bcs.getExportedCallStats().size());
- BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
- assertEquals(CALLING_UID, stat.workSourceUid);
- assertEquals(CALLING_UID, stat.callingUid);
- }
-
- @Test
public void testGetExportedStatsWithoutCalls() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
Binder binder = new Binder();
@@ -565,15 +549,15 @@
bcs.setMaxBinderCallStats(2);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
@@ -590,12 +574,15 @@
bcs.setMaxBinderCallStats(1);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 2);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ // Should use the same overflow entry.
+ callSession = bcs.callStarted(binder, 3, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
assertEquals(2, callStatsList.size());
@@ -606,20 +593,79 @@
assertEquals(CALLING_UID, callStats.callingUid);
callStats = callStatsList.get(1);
- assertEquals(1, callStats.callCount);
+ assertEquals(2, callStats.callCount);
assertEquals("-1", callStats.methodName);
assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
callStats.className);
- assertEquals(CALLING_UID, callStats.callingUid);
+ assertEquals(false , callStats.screenInteractive);
+ assertEquals(-1 , callStats.callingUid);
+ }
+
+ @Test
+ public void testOverflow_oneOverflowEntryPerUid() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(1);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID + 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID + 1);
+
+ // Different uids have different overflow entries.
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID + 2);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID + 2);
+
+ List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
+ assertEquals(3, callStatsList.size());
+
+ BinderCallsStats.ExportedCallStat callStats = callStatsList.get(1);
+ assertEquals(WORKSOURCE_UID + 1, callStats.workSourceUid);
+ assertEquals(1, callStats.callCount);
+ assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+ callStats.className);
+
+ callStats = callStatsList.get(2);
+ assertEquals(WORKSOURCE_UID + 2, callStats.workSourceUid);
+ assertEquals(1, callStats.callCount);
+ assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+ callStats.className);
+ }
+
+ @Test
+ public void testAddsDebugEntries() {
+ long startTime = System.currentTimeMillis();
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setAddDebugEntries(true);
+ ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats();
+ assertEquals(3, callStats.size());
+ BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0);
+ assertEquals("", debugEntry1.className);
+ assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName);
+ assertTrue(startTime <= debugEntry1.latencyMicros);
+ BinderCallsStats.ExportedCallStat debugEntry2 = callStats.get(1);
+ assertEquals("", debugEntry2.className);
+ assertEquals("__DEBUG_end_time_millis", debugEntry2.methodName);
+ assertTrue(debugEntry1.latencyMicros <= debugEntry2.latencyMicros);
+ BinderCallsStats.ExportedCallStat debugEntry3 = callStats.get(2);
+ assertEquals("", debugEntry3.className);
+ assertEquals("__DEBUG_battery_time_millis", debugEntry3.methodName);
+ assertTrue(debugEntry3.latencyMicros >= 0);
}
class TestBinderCallsStats extends BinderCallsStats {
public int callingUid = CALLING_UID;
- public int workSourceUid = WORKSOURCE_UID;
public long time = 1234;
public long elapsedTime = 0;
TestBinderCallsStats() {
+ this(mDeviceState);
+ }
+
+ TestBinderCallsStats(CachedDeviceState deviceState) {
// Make random generator not random.
super(new Injector() {
public Random getRandomGenerator() {
@@ -633,7 +679,10 @@
}
});
setSamplingInterval(1);
- setDeviceState(mDeviceState.getReadonlyClient());
+ setAddDebugEntries(false);
+ if (deviceState != null) {
+ setDeviceState(deviceState.getReadonlyClient());
+ }
}
@Override
@@ -650,11 +699,6 @@
protected int getCallingUid() {
return callingUid;
}
-
- @Override
- protected int getWorkSourceUid() {
- return workSourceUid;
- }
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 31dde5c..f26dfad 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -273,8 +273,7 @@
@Test
public void testDataNotCollectedBeforeDeviceStateSet() {
- TestableLooperStats looperStats = new TestableLooperStats(1, 100);
- looperStats.setDeviceState(null);
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100, null);
Object token1 = looperStats.messageDispatchStarting();
looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
@@ -439,15 +438,19 @@
looperStats.messageDispatched(token, message);
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
- assertThat(entries).hasSize(3);
+ assertThat(entries).hasSize(4);
LooperStats.ExportedEntry debugEntry1 = entries.get(1);
assertThat(debugEntry1.handlerClassName).isEqualTo("");
assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
- assertThat(debugEntry1.maxDelayMillis).isEqualTo(looperStats.getStartTimeMillis());
+ assertThat(debugEntry1.totalLatencyMicros).isEqualTo(looperStats.getStartTimeMillis());
LooperStats.ExportedEntry debugEntry2 = entries.get(2);
assertThat(debugEntry2.handlerClassName).isEqualTo("");
assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
- assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis());
+ assertThat(debugEntry2.totalLatencyMicros).isAtLeast(looperStats.getStartTimeMillis());
+ LooperStats.ExportedEntry debugEntry3 = entries.get(3);
+ assertThat(debugEntry3.handlerClassName).isEqualTo("");
+ assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis");
+ assertThat(debugEntry3.totalLatencyMicros).isAtLeast(0L);
}
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
@@ -468,10 +471,16 @@
private int mSamplingInterval;
TestableLooperStats(int samplingInterval, int sizeCap) {
+ this(samplingInterval, sizeCap, mDeviceState);
+ }
+
+ TestableLooperStats(int samplingInterval, int sizeCap, CachedDeviceState deviceState) {
super(samplingInterval, sizeCap);
- this.mSamplingInterval = samplingInterval;
- this.setDeviceState(mDeviceState.getReadonlyClient());
- this.setAddDebugEntries(false);
+ mSamplingInterval = samplingInterval;
+ setAddDebugEntries(false);
+ if (deviceState != null) {
+ setDeviceState(deviceState.getReadonlyClient());
+ }
}
void tickRealtime(long micros) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 141948f..4a2db0a 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -225,15 +225,18 @@
<library name="android.test.base"
file="/system/framework/android.test.base.impl.jar" />
<library name="android.test.mock"
- file="/system/framework/android.test.mock.impl.jar" />
+ file="/system/framework/android.test.mock.impl.jar"
+ dependency="android.test.base" />
<library name="android.test.runner"
- file="/system/framework/android.test.runner.impl.jar" />
+ file="/system/framework/android.test.runner.impl.jar"
+ dependency="android.test.base:android.test.mock" />
<!-- In BOOT_JARS historically, and now added to legacy applications. -->
<library name="android.hidl.base-V1.0-java"
file="/system/framework/android.hidl.base-V1.0-java.jar" />
<library name="android.hidl.manager-V1.0-java"
- file="/system/framework/android.hidl.manager-V1.0-java.jar" />
+ file="/system/framework/android.hidl.manager-V1.0-java.jar"
+ dependency="android.hidl.base-V1.0-java" />
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9e4ea32..dcf95fd 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -141,6 +141,8 @@
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+ <permission name="android.permission.CLEAR_APP_USER_DATA"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
</privapp-permissions>
<privapp-permissions package="com.android.permissioncontroller">
@@ -281,6 +283,7 @@
<permission name="android.permission.WRITE_APN_SETTINGS"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
</privapp-permissions>
<privapp-permissions package="com.android.settings.intelligence">
@@ -408,6 +411,7 @@
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+ <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
</privapp-permissions>
<privapp-permissions package="com.android.tv">
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index bf114b9..2227cf5 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1779,6 +1779,36 @@
}
/**
+ * <p>Computes the chromaticity coordinates of a CIE series D illuminant
+ * from the specified correlated color temperature (CCT). The specified CCT
+ * must be greater than 0. A meaningful CCT range is [4000, 25000].</p>
+ *
+ * <p>The transform is computed using the methods referred to in Kang et
+ * al., <i>Design of Advanced Color - Temperature Control System for HDTV
+ * Applications</i>, Journal of Korean Physical Society 41, 865-871
+ * (2002).</p>
+ *
+ * @param cct The correlated color temperature, in Kelvin
+ * @return Corresponding XYZ values
+ * @throws IllegalArgumentException If cct is invalid
+ */
+ @NonNull
+ @Size(3)
+ public static float[] cctToIlluminantdXyz(@IntRange(from = 1) int cct) {
+ if (cct < 1) {
+ throw new IllegalArgumentException("Temperature must be greater than 0");
+ }
+
+ final float icct = 1.0f / cct;
+ final float icct2 = icct * icct;
+ final float x = cct <= 7000.0f ?
+ 0.244063f + 0.09911e3f * icct + 2.9678e6f * icct2 - 4.6070e9f * icct2 * icct :
+ 0.237040f + 0.24748e3f * icct + 1.9018e6f * icct2 - 2.0064e9f * icct2 * icct;
+ final float y = -3.0f * x * x + 2.87f * x - 0.275f;
+ return xyYToXyz(new float[] {x, y});
+ }
+
+ /**
* <p>Computes the chromatic adaptation transform from the specified
* source white point to the specified destination white point.</p>
*
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index dea2f45..cb12a7c 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -150,11 +151,14 @@
*/
AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
mLayerState = createConstantState(state, res);
-
- if (sMask == null) {
- sMask = PathParser.createPathFromPathData(
- Resources.getSystem().getString(R.string.config_icon_mask));
- }
+ // config_icon_mask from context bound resource may have been chaged using
+ // OverlayManager. Read that one first.
+ Resources r = ActivityThread.currentActivityThread() == null
+ ? Resources.getSystem()
+ : ActivityThread.currentActivityThread().getApplication().getResources();
+ // TODO: either make sMask update only when config_icon_mask changes OR
+ // get rid of it all-together in layoutlib
+ sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
mMask = new Path(sMask);
mMaskScaleOnly = new Path(mMask);
mMaskMatrix = new Matrix();
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 4ee45bf..70c90eb 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -65,7 +65,7 @@
*/
public DrawableWrapper(@Nullable Drawable dr) {
mState = null;
- mDrawable = dr;
+ setDrawable(dr);
}
/**
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 6f78651..2536619 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.Px;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -49,12 +50,17 @@
*/
public class MeasuredText {
private long mNativePtr;
+ private boolean mComputeHyphenation;
+ private boolean mComputeLayout;
private @NonNull char[] mChars;
// Use builder instead.
- private MeasuredText(long ptr, @NonNull char[] chars) {
+ private MeasuredText(long ptr, @NonNull char[] chars, boolean computeHyphenation,
+ boolean computeLayout) {
mNativePtr = ptr;
mChars = chars;
+ mComputeHyphenation = computeHyphenation;
+ mComputeLayout = computeLayout;
}
/**
@@ -172,6 +178,7 @@
private boolean mComputeHyphenation = false;
private boolean mComputeLayout = true;
private int mCurrentOffset = 0;
+ private @Nullable MeasuredText mHintMt = null;
/**
* Construct a builder.
@@ -188,6 +195,27 @@
}
/**
+ * Construct a builder with existing MeasuredText.
+ *
+ * The MeasuredText returned by build method will hold a reference of the text. Developer is
+ * not supposed to modify the text.
+ *
+ * @param text a text
+ */
+ public Builder(@NonNull MeasuredText text) {
+ Preconditions.checkNotNull(text);
+ mText = text.mChars;
+ mNativePtr = nInitBuilder();
+ if (!text.mComputeLayout) {
+ throw new IllegalArgumentException(
+ "The input MeasuredText must not be created with setComputeLayout(false).");
+ }
+ mComputeHyphenation = text.mComputeHyphenation;
+ mComputeLayout = text.mComputeLayout;
+ mHintMt = text;
+ }
+
+ /**
* Apply styles to the given length.
*
* Keeps an internal offset which increases at every append. The initial value for this
@@ -282,10 +310,16 @@
if (mCurrentOffset != mText.length) {
throw new IllegalStateException("Style info has not been provided for all text.");
}
+ if (mHintMt != null && mHintMt.mComputeHyphenation != mComputeHyphenation) {
+ throw new IllegalArgumentException(
+ "The hyphenation configuration is different from given hint MeasuredText");
+ }
try {
- long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation,
+ long hintPtr = (mHintMt == null) ? 0 : mHintMt.getNativePtr();
+ long ptr = nBuildMeasuredText(mNativePtr, hintPtr, mText, mComputeHyphenation,
mComputeLayout);
- MeasuredText res = new MeasuredText(ptr, mText);
+ final MeasuredText res = new MeasuredText(ptr, mText, mComputeHyphenation,
+ mComputeLayout);
sRegistry.registerNativeAllocation(res, ptr);
return res;
} finally {
@@ -339,6 +373,7 @@
private static native long nBuildMeasuredText(
/* Non Zero */ long nativeBuilderPtr,
+ long hintMtPtr,
@NonNull char[] text,
boolean computeHyphenation,
boolean computeLayout);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7ab12b1..ad9ec02 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -217,10 +217,19 @@
ATRACE_NAME("AssetManager::GetResourceConfigurations");
std::set<ResTable_config> configurations;
for (const PackageGroup& package_group : package_groups_) {
+ bool found_system_package = false;
for (const ConfiguredPackage& package : package_group.packages_) {
if (exclude_system && package.loaded_package_->IsSystem()) {
+ found_system_package = true;
continue;
}
+
+ if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
+ // Overlays must appear after the target package to take effect. Any overlay found in the
+ // same package as a system package is able to overlay system resources.
+ continue;
+ }
+
package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
}
}
@@ -232,10 +241,19 @@
ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
for (const PackageGroup& package_group : package_groups_) {
+ bool found_system_package = false;
for (const ConfiguredPackage& package : package_group.packages_) {
if (exclude_system && package.loaded_package_->IsSystem()) {
+ found_system_package = true;
continue;
}
+
+ if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
+ // Overlays must appear after the target package to take effect. Any overlay found in the
+ // same package as a system package is able to overlay system resources.
+ continue;
+ }
+
package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
}
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 68d216d..c20c720 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -583,7 +583,65 @@
loaded_package->dynamic_package_map_.emplace_back(std::move(package_name),
dtohl(entry_iter->packageId));
}
+ } break;
+ case RES_TABLE_OVERLAYABLE_TYPE: {
+ const ResTable_overlayable_header* header =
+ child_chunk.header<ResTable_overlayable_header>();
+ if (header == nullptr) {
+ LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
+ return {};
+ }
+
+ // Iterate over the overlayable policy chunks
+ ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
+ while (overlayable_iter.HasNext()) {
+ const Chunk overlayable_child_chunk = overlayable_iter.Next();
+
+ switch (overlayable_child_chunk.type()) {
+ case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
+ const ResTable_overlayable_policy_header* policy_header =
+ overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
+ if (policy_header == nullptr) {
+ LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
+ return {};
+ }
+
+ if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
+ < dtohl(policy_header->entry_count)) {
+ LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
+ return {};
+ }
+
+ // Retrieve all the ids belonging to this policy
+ std::unordered_set<uint32_t> ids;
+ const auto ids_begin =
+ reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
+ const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
+ for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
+ ids.insert(dtohl(id_iter->ident));
+ }
+
+ // Add the pairing of overlayable properties to resource ids to the package
+ OverlayableInfo overlayable_info;
+ overlayable_info.policy_flags = policy_header->policy_flags;
+ loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
+ break;
+ }
+
+ default:
+ LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ break;
+ }
+ }
+
+ if (overlayable_iter.HadError()) {
+ LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s",
+ overlayable_iter.GetLastError().c_str());
+ if (overlayable_iter.HadFatalError()) {
+ return {};
+ }
+ }
} break;
default:
diff --git a/libs/androidfw/LocaleData.cpp b/libs/androidfw/LocaleData.cpp
index 889d166..020cef6 100644
--- a/libs/androidfw/LocaleData.cpp
+++ b/libs/androidfw/LocaleData.cpp
@@ -34,11 +34,11 @@
}
inline uint32_t dropRegion(uint32_t packed_locale) {
- return packed_locale & 0xFFFF0000lu;
+ return packed_locale & 0xFFFF0000LU;
}
inline bool hasRegion(uint32_t packed_locale) {
- return (packed_locale & 0x0000FFFFlu) != 0;
+ return (packed_locale & 0x0000FFFFLU) != 0;
}
const size_t SCRIPT_LENGTH = 4;
@@ -122,9 +122,9 @@
return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
}
-const uint32_t US_SPANISH = 0x65735553lu; // es-US
-const uint32_t MEXICAN_SPANISH = 0x65734D58lu; // es-MX
-const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424lu; // es-419
+const uint32_t US_SPANISH = 0x65735553LU; // es-US
+const uint32_t MEXICAN_SPANISH = 0x65734D58LU; // es-MX
+const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424LU; // es-419
// The two locales es-US and es-MX are treated as special fallbacks for es-419.
// If there is no es-419, they are considered its equivalent.
@@ -225,8 +225,8 @@
}
const uint32_t ENGLISH_STOP_LIST[2] = {
- 0x656E0000lu, // en
- 0x656E8400lu, // en-001
+ 0x656E0000LU, // en
+ 0x656E8400LU, // en-001
};
const char ENGLISH_CHARS[2] = {'e', 'n'};
const char LATIN_CHARS[4] = {'L', 'a', 't', 'n'};
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 7c381ef..c276a23 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -1446,733 +1446,733 @@
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
- 0x616145544C61746Ellu, // aa_Latn_ET
- 0x616247454379726Cllu, // ab_Cyrl_GE
- 0xC42047484C61746Ellu, // abr_Latn_GH
- 0x904049444C61746Ellu, // ace_Latn_ID
- 0x9C4055474C61746Ellu, // ach_Latn_UG
- 0x806047484C61746Ellu, // ada_Latn_GH
- 0xE06052554379726Cllu, // ady_Cyrl_RU
- 0x6165495241767374llu, // ae_Avst_IR
- 0x8480544E41726162llu, // aeb_Arab_TN
- 0x61665A414C61746Ellu, // af_Latn_ZA
- 0xC0C0434D4C61746Ellu, // agq_Latn_CM
- 0xB8E0494E41686F6Dllu, // aho_Ahom_IN
- 0x616B47484C61746Ellu, // ak_Latn_GH
- 0xA940495158737578llu, // akk_Xsux_IQ
- 0xB560584B4C61746Ellu, // aln_Latn_XK
- 0xCD6052554379726Cllu, // alt_Cyrl_RU
- 0x616D455445746869llu, // am_Ethi_ET
- 0xB9804E474C61746Ellu, // amo_Latn_NG
- 0xE5C049444C61746Ellu, // aoz_Latn_ID
- 0x8DE0544741726162llu, // apd_Arab_TG
- 0x6172454741726162llu, // ar_Arab_EG
- 0x8A20495241726D69llu, // arc_Armi_IR
- 0x8A204A4F4E626174llu, // arc_Nbat_JO
- 0x8A20535950616C6Dllu, // arc_Palm_SY
- 0xB620434C4C61746Ellu, // arn_Latn_CL
- 0xBA20424F4C61746Ellu, // aro_Latn_BO
- 0xC220445A41726162llu, // arq_Arab_DZ
- 0xE2204D4141726162llu, // ary_Arab_MA
- 0xE620454741726162llu, // arz_Arab_EG
- 0x6173494E42656E67llu, // as_Beng_IN
- 0x8240545A4C61746Ellu, // asa_Latn_TZ
- 0x9240555353676E77llu, // ase_Sgnw_US
- 0xCE4045534C61746Ellu, // ast_Latn_ES
- 0xA66043414C61746Ellu, // atj_Latn_CA
- 0x617652554379726Cllu, // av_Cyrl_RU
- 0x82C0494E44657661llu, // awa_Deva_IN
- 0x6179424F4C61746Ellu, // ay_Latn_BO
- 0x617A495241726162llu, // az_Arab_IR
- 0x617A415A4C61746Ellu, // az_Latn_AZ
- 0x626152554379726Cllu, // ba_Cyrl_RU
- 0xAC01504B41726162llu, // bal_Arab_PK
- 0xB40149444C61746Ellu, // ban_Latn_ID
- 0xBC014E5044657661llu, // bap_Deva_NP
- 0xC40141544C61746Ellu, // bar_Latn_AT
- 0xC801434D4C61746Ellu, // bas_Latn_CM
- 0xDC01434D42616D75llu, // bax_Bamu_CM
- 0x882149444C61746Ellu, // bbc_Latn_ID
- 0xA421434D4C61746Ellu, // bbj_Latn_CM
- 0xA04143494C61746Ellu, // bci_Latn_CI
- 0x626542594379726Cllu, // be_Cyrl_BY
- 0xA481534441726162llu, // bej_Arab_SD
- 0xB0815A4D4C61746Ellu, // bem_Latn_ZM
- 0xD88149444C61746Ellu, // bew_Latn_ID
- 0xE481545A4C61746Ellu, // bez_Latn_TZ
- 0x8CA1434D4C61746Ellu, // bfd_Latn_CM
- 0xC0A1494E54616D6Cllu, // bfq_Taml_IN
- 0xCCA1504B41726162llu, // bft_Arab_PK
- 0xE0A1494E44657661llu, // bfy_Deva_IN
- 0x626742474379726Cllu, // bg_Cyrl_BG
- 0x88C1494E44657661llu, // bgc_Deva_IN
- 0xB4C1504B41726162llu, // bgn_Arab_PK
- 0xDCC154524772656Bllu, // bgx_Grek_TR
- 0x84E1494E44657661llu, // bhb_Deva_IN
- 0xA0E1494E44657661llu, // bhi_Deva_IN
- 0xA8E150484C61746Ellu, // bhk_Latn_PH
- 0xB8E1494E44657661llu, // bho_Deva_IN
- 0x626956554C61746Ellu, // bi_Latn_VU
- 0xA90150484C61746Ellu, // bik_Latn_PH
- 0xB5014E474C61746Ellu, // bin_Latn_NG
- 0xA521494E44657661llu, // bjj_Deva_IN
- 0xB52149444C61746Ellu, // bjn_Latn_ID
- 0xB141434D4C61746Ellu, // bkm_Latn_CM
- 0xD14150484C61746Ellu, // bku_Latn_PH
- 0xCD61564E54617674llu, // blt_Tavt_VN
- 0x626D4D4C4C61746Ellu, // bm_Latn_ML
- 0xC1814D4C4C61746Ellu, // bmq_Latn_ML
- 0x626E424442656E67llu, // bn_Beng_BD
- 0x626F434E54696274llu, // bo_Tibt_CN
- 0xE1E1494E42656E67llu, // bpy_Beng_IN
- 0xA201495241726162llu, // bqi_Arab_IR
- 0xD60143494C61746Ellu, // bqv_Latn_CI
- 0x627246524C61746Ellu, // br_Latn_FR
- 0x8221494E44657661llu, // bra_Deva_IN
- 0x9E21504B41726162llu, // brh_Arab_PK
- 0xDE21494E44657661llu, // brx_Deva_IN
- 0x627342414C61746Ellu, // bs_Latn_BA
- 0xC2414C5242617373llu, // bsq_Bass_LR
- 0xCA41434D4C61746Ellu, // bss_Latn_CM
- 0xBA6150484C61746Ellu, // bto_Latn_PH
- 0xD661504B44657661llu, // btv_Deva_PK
- 0x828152554379726Cllu, // bua_Cyrl_RU
- 0x8A8159544C61746Ellu, // buc_Latn_YT
- 0x9A8149444C61746Ellu, // bug_Latn_ID
- 0xB281434D4C61746Ellu, // bum_Latn_CM
- 0x86A147514C61746Ellu, // bvb_Latn_GQ
- 0xB701455245746869llu, // byn_Ethi_ER
- 0xD701434D4C61746Ellu, // byv_Latn_CM
- 0x93214D4C4C61746Ellu, // bze_Latn_ML
- 0x636145534C61746Ellu, // ca_Latn_ES
- 0x9C424E474C61746Ellu, // cch_Latn_NG
- 0xBC42494E42656E67llu, // ccp_Beng_IN
- 0xBC42424443616B6Dllu, // ccp_Cakm_BD
- 0x636552554379726Cllu, // ce_Cyrl_RU
- 0x848250484C61746Ellu, // ceb_Latn_PH
- 0x98C255474C61746Ellu, // cgg_Latn_UG
- 0x636847554C61746Ellu, // ch_Latn_GU
- 0xA8E2464D4C61746Ellu, // chk_Latn_FM
- 0xB0E252554379726Cllu, // chm_Cyrl_RU
- 0xB8E255534C61746Ellu, // cho_Latn_US
- 0xBCE243414C61746Ellu, // chp_Latn_CA
- 0xC4E2555343686572llu, // chr_Cher_US
- 0x81224B4841726162llu, // cja_Arab_KH
- 0xB122564E4368616Dllu, // cjm_Cham_VN
- 0x8542495141726162llu, // ckb_Arab_IQ
- 0x636F46524C61746Ellu, // co_Latn_FR
- 0xBDC24547436F7074llu, // cop_Copt_EG
- 0xC9E250484C61746Ellu, // cps_Latn_PH
- 0x6372434143616E73llu, // cr_Cans_CA
- 0xA622434143616E73llu, // crj_Cans_CA
- 0xAA22434143616E73llu, // crk_Cans_CA
- 0xAE22434143616E73llu, // crl_Cans_CA
- 0xB222434143616E73llu, // crm_Cans_CA
- 0xCA2253434C61746Ellu, // crs_Latn_SC
- 0x6373435A4C61746Ellu, // cs_Latn_CZ
- 0x8642504C4C61746Ellu, // csb_Latn_PL
- 0xDA42434143616E73llu, // csw_Cans_CA
- 0x8E624D4D50617563llu, // ctd_Pauc_MM
- 0x637552554379726Cllu, // cu_Cyrl_RU
- 0x63754247476C6167llu, // cu_Glag_BG
- 0x637652554379726Cllu, // cv_Cyrl_RU
- 0x637947424C61746Ellu, // cy_Latn_GB
- 0x6461444B4C61746Ellu, // da_Latn_DK
- 0xA80355534C61746Ellu, // dak_Latn_US
- 0xC40352554379726Cllu, // dar_Cyrl_RU
- 0xD4034B454C61746Ellu, // dav_Latn_KE
- 0x8843494E41726162llu, // dcc_Arab_IN
- 0x646544454C61746Ellu, // de_Latn_DE
- 0xB48343414C61746Ellu, // den_Latn_CA
- 0xC4C343414C61746Ellu, // dgr_Latn_CA
- 0x91234E454C61746Ellu, // dje_Latn_NE
- 0xA5A343494C61746Ellu, // dnj_Latn_CI
- 0xA1C3494E41726162llu, // doi_Arab_IN
- 0x864344454C61746Ellu, // dsb_Latn_DE
- 0xB2634D4C4C61746Ellu, // dtm_Latn_ML
- 0xBE634D594C61746Ellu, // dtp_Latn_MY
- 0xE2634E5044657661llu, // dty_Deva_NP
- 0x8283434D4C61746Ellu, // dua_Latn_CM
- 0x64764D5654686161llu, // dv_Thaa_MV
- 0xBB03534E4C61746Ellu, // dyo_Latn_SN
- 0xD30342464C61746Ellu, // dyu_Latn_BF
- 0x647A425454696274llu, // dz_Tibt_BT
- 0xD0244B454C61746Ellu, // ebu_Latn_KE
- 0x656547484C61746Ellu, // ee_Latn_GH
- 0xA0A44E474C61746Ellu, // efi_Latn_NG
- 0xACC449544C61746Ellu, // egl_Latn_IT
- 0xE0C4454745677970llu, // egy_Egyp_EG
- 0xE1444D4D4B616C69llu, // eky_Kali_MM
- 0x656C47524772656Bllu, // el_Grek_GR
- 0x656E47424C61746Ellu, // en_Latn_GB
- 0x656E55534C61746Ellu, // en_Latn_US
- 0x656E474253686177llu, // en_Shaw_GB
- 0x657345534C61746Ellu, // es_Latn_ES
- 0x65734D584C61746Ellu, // es_Latn_MX
- 0x657355534C61746Ellu, // es_Latn_US
- 0xD24455534C61746Ellu, // esu_Latn_US
- 0x657445454C61746Ellu, // et_Latn_EE
- 0xCE6449544974616Cllu, // ett_Ital_IT
- 0x657545534C61746Ellu, // eu_Latn_ES
- 0xBAC4434D4C61746Ellu, // ewo_Latn_CM
- 0xCEE445534C61746Ellu, // ext_Latn_ES
- 0x6661495241726162llu, // fa_Arab_IR
- 0xB40547514C61746Ellu, // fan_Latn_GQ
- 0x6666474E41646C6Dllu, // ff_Adlm_GN
- 0x6666534E4C61746Ellu, // ff_Latn_SN
- 0xB0A54D4C4C61746Ellu, // ffm_Latn_ML
- 0x666946494C61746Ellu, // fi_Latn_FI
- 0x8105534441726162llu, // fia_Arab_SD
- 0xAD0550484C61746Ellu, // fil_Latn_PH
- 0xCD0553454C61746Ellu, // fit_Latn_SE
- 0x666A464A4C61746Ellu, // fj_Latn_FJ
- 0x666F464F4C61746Ellu, // fo_Latn_FO
- 0xB5C5424A4C61746Ellu, // fon_Latn_BJ
- 0x667246524C61746Ellu, // fr_Latn_FR
- 0x8A2555534C61746Ellu, // frc_Latn_US
- 0xBE2546524C61746Ellu, // frp_Latn_FR
- 0xC62544454C61746Ellu, // frr_Latn_DE
- 0xCA2544454C61746Ellu, // frs_Latn_DE
- 0x8685434D41726162llu, // fub_Arab_CM
- 0x8E8557464C61746Ellu, // fud_Latn_WF
- 0x9685474E4C61746Ellu, // fuf_Latn_GN
- 0xC2854E454C61746Ellu, // fuq_Latn_NE
- 0xC68549544C61746Ellu, // fur_Latn_IT
- 0xD6854E474C61746Ellu, // fuv_Latn_NG
- 0xC6A553444C61746Ellu, // fvr_Latn_SD
- 0x66794E4C4C61746Ellu, // fy_Latn_NL
- 0x676149454C61746Ellu, // ga_Latn_IE
- 0x800647484C61746Ellu, // gaa_Latn_GH
- 0x98064D444C61746Ellu, // gag_Latn_MD
- 0xB406434E48616E73llu, // gan_Hans_CN
- 0xE00649444C61746Ellu, // gay_Latn_ID
- 0xB026494E44657661llu, // gbm_Deva_IN
- 0xE426495241726162llu, // gbz_Arab_IR
- 0xC44647464C61746Ellu, // gcr_Latn_GF
- 0x676447424C61746Ellu, // gd_Latn_GB
- 0xE486455445746869llu, // gez_Ethi_ET
- 0xB4C64E5044657661llu, // ggn_Deva_NP
- 0xAD064B494C61746Ellu, // gil_Latn_KI
- 0xA926504B41726162llu, // gjk_Arab_PK
- 0xD126504B41726162llu, // gju_Arab_PK
- 0x676C45534C61746Ellu, // gl_Latn_ES
- 0xA966495241726162llu, // glk_Arab_IR
- 0x676E50594C61746Ellu, // gn_Latn_PY
- 0xB1C6494E44657661llu, // gom_Deva_IN
- 0xB5C6494E54656C75llu, // gon_Telu_IN
- 0xC5C649444C61746Ellu, // gor_Latn_ID
- 0xC9C64E4C4C61746Ellu, // gos_Latn_NL
- 0xCDC65541476F7468llu, // got_Goth_UA
- 0x8A26435943707274llu, // grc_Cprt_CY
- 0x8A2647524C696E62llu, // grc_Linb_GR
- 0xCE26494E42656E67llu, // grt_Beng_IN
- 0xDA4643484C61746Ellu, // gsw_Latn_CH
- 0x6775494E47756A72llu, // gu_Gujr_IN
- 0x868642524C61746Ellu, // gub_Latn_BR
- 0x8A86434F4C61746Ellu, // guc_Latn_CO
- 0xC68647484C61746Ellu, // gur_Latn_GH
- 0xE6864B454C61746Ellu, // guz_Latn_KE
- 0x6776494D4C61746Ellu, // gv_Latn_IM
- 0xC6A64E5044657661llu, // gvr_Deva_NP
- 0xA2C643414C61746Ellu, // gwi_Latn_CA
- 0x68614E474C61746Ellu, // ha_Latn_NG
- 0xA807434E48616E73llu, // hak_Hans_CN
- 0xD80755534C61746Ellu, // haw_Latn_US
- 0xE407414641726162llu, // haz_Arab_AF
- 0x6865494C48656272llu, // he_Hebr_IL
- 0x6869494E44657661llu, // hi_Deva_IN
- 0x9507464A4C61746Ellu, // hif_Latn_FJ
- 0xAD0750484C61746Ellu, // hil_Latn_PH
- 0xD1675452486C7577llu, // hlu_Hluw_TR
- 0x8D87434E506C7264llu, // hmd_Plrd_CN
- 0x8DA7504B41726162llu, // hnd_Arab_PK
- 0x91A7494E44657661llu, // hne_Deva_IN
- 0xA5A74C41486D6E67llu, // hnj_Hmng_LA
- 0xB5A750484C61746Ellu, // hnn_Latn_PH
- 0xB9A7504B41726162llu, // hno_Arab_PK
- 0x686F50474C61746Ellu, // ho_Latn_PG
- 0x89C7494E44657661llu, // hoc_Deva_IN
- 0xA5C7494E44657661llu, // hoj_Deva_IN
- 0x687248524C61746Ellu, // hr_Latn_HR
- 0x864744454C61746Ellu, // hsb_Latn_DE
- 0xB647434E48616E73llu, // hsn_Hans_CN
- 0x687448544C61746Ellu, // ht_Latn_HT
- 0x687548554C61746Ellu, // hu_Latn_HU
- 0x6879414D41726D6Ellu, // hy_Armn_AM
- 0x687A4E414C61746Ellu, // hz_Latn_NA
- 0x696146524C61746Ellu, // ia_Latn_FR
- 0x80284D594C61746Ellu, // iba_Latn_MY
- 0x84284E474C61746Ellu, // ibb_Latn_NG
- 0x696449444C61746Ellu, // id_Latn_ID
- 0x69674E474C61746Ellu, // ig_Latn_NG
- 0x6969434E59696969llu, // ii_Yiii_CN
- 0x696B55534C61746Ellu, // ik_Latn_US
- 0xCD4843414C61746Ellu, // ikt_Latn_CA
- 0xB96850484C61746Ellu, // ilo_Latn_PH
- 0x696E49444C61746Ellu, // in_Latn_ID
- 0x9DA852554379726Cllu, // inh_Cyrl_RU
- 0x697349534C61746Ellu, // is_Latn_IS
- 0x697449544C61746Ellu, // it_Latn_IT
- 0x6975434143616E73llu, // iu_Cans_CA
- 0x6977494C48656272llu, // iw_Hebr_IL
- 0x9F2852554C61746Ellu, // izh_Latn_RU
- 0x6A614A504A70616Ellu, // ja_Jpan_JP
- 0xB0094A4D4C61746Ellu, // jam_Latn_JM
- 0xB8C9434D4C61746Ellu, // jgo_Latn_CM
- 0x8989545A4C61746Ellu, // jmc_Latn_TZ
- 0xAD894E5044657661llu, // jml_Deva_NP
- 0xCE89444B4C61746Ellu, // jut_Latn_DK
- 0x6A7649444C61746Ellu, // jv_Latn_ID
- 0x6A7749444C61746Ellu, // jw_Latn_ID
- 0x6B61474547656F72llu, // ka_Geor_GE
- 0x800A555A4379726Cllu, // kaa_Cyrl_UZ
- 0x840A445A4C61746Ellu, // kab_Latn_DZ
- 0x880A4D4D4C61746Ellu, // kac_Latn_MM
- 0xA40A4E474C61746Ellu, // kaj_Latn_NG
- 0xB00A4B454C61746Ellu, // kam_Latn_KE
- 0xB80A4D4C4C61746Ellu, // kao_Latn_ML
- 0x8C2A52554379726Cllu, // kbd_Cyrl_RU
- 0xE02A4E4541726162llu, // kby_Arab_NE
- 0x984A4E474C61746Ellu, // kcg_Latn_NG
- 0xA84A5A574C61746Ellu, // kck_Latn_ZW
- 0x906A545A4C61746Ellu, // kde_Latn_TZ
- 0x9C6A544741726162llu, // kdh_Arab_TG
- 0xCC6A544854686169llu, // kdt_Thai_TH
- 0x808A43564C61746Ellu, // kea_Latn_CV
- 0xB48A434D4C61746Ellu, // ken_Latn_CM
- 0xB8AA43494C61746Ellu, // kfo_Latn_CI
- 0xC4AA494E44657661llu, // kfr_Deva_IN
- 0xE0AA494E44657661llu, // kfy_Deva_IN
- 0x6B6743444C61746Ellu, // kg_Latn_CD
- 0x90CA49444C61746Ellu, // kge_Latn_ID
- 0xBCCA42524C61746Ellu, // kgp_Latn_BR
- 0x80EA494E4C61746Ellu, // kha_Latn_IN
- 0x84EA434E54616C75llu, // khb_Talu_CN
- 0xB4EA494E44657661llu, // khn_Deva_IN
- 0xC0EA4D4C4C61746Ellu, // khq_Latn_ML
- 0xCCEA494E4D796D72llu, // kht_Mymr_IN
- 0xD8EA504B41726162llu, // khw_Arab_PK
- 0x6B694B454C61746Ellu, // ki_Latn_KE
- 0xD10A54524C61746Ellu, // kiu_Latn_TR
- 0x6B6A4E414C61746Ellu, // kj_Latn_NA
- 0x992A4C414C616F6Fllu, // kjg_Laoo_LA
- 0x6B6B434E41726162llu, // kk_Arab_CN
- 0x6B6B4B5A4379726Cllu, // kk_Cyrl_KZ
- 0xA54A434D4C61746Ellu, // kkj_Latn_CM
- 0x6B6C474C4C61746Ellu, // kl_Latn_GL
- 0xB56A4B454C61746Ellu, // kln_Latn_KE
- 0x6B6D4B484B686D72llu, // km_Khmr_KH
- 0x858A414F4C61746Ellu, // kmb_Latn_AO
- 0x6B6E494E4B6E6461llu, // kn_Knda_IN
- 0x6B6F4B524B6F7265llu, // ko_Kore_KR
- 0xA1CA52554379726Cllu, // koi_Cyrl_RU
- 0xA9CA494E44657661llu, // kok_Deva_IN
- 0xC9CA464D4C61746Ellu, // kos_Latn_FM
- 0x91EA4C524C61746Ellu, // kpe_Latn_LR
- 0x8A2A52554379726Cllu, // krc_Cyrl_RU
- 0xA22A534C4C61746Ellu, // kri_Latn_SL
- 0xA62A50484C61746Ellu, // krj_Latn_PH
- 0xAE2A52554C61746Ellu, // krl_Latn_RU
- 0xD22A494E44657661llu, // kru_Deva_IN
- 0x6B73494E41726162llu, // ks_Arab_IN
- 0x864A545A4C61746Ellu, // ksb_Latn_TZ
- 0x964A434D4C61746Ellu, // ksf_Latn_CM
- 0x9E4A44454C61746Ellu, // ksh_Latn_DE
- 0x6B75495141726162llu, // ku_Arab_IQ
- 0x6B7554524C61746Ellu, // ku_Latn_TR
- 0xB28A52554379726Cllu, // kum_Cyrl_RU
- 0x6B7652554379726Cllu, // kv_Cyrl_RU
- 0xC6AA49444C61746Ellu, // kvr_Latn_ID
- 0xDEAA504B41726162llu, // kvx_Arab_PK
- 0x6B7747424C61746Ellu, // kw_Latn_GB
- 0xB2EA544854686169llu, // kxm_Thai_TH
- 0xBEEA504B41726162llu, // kxp_Arab_PK
- 0x6B79434E41726162llu, // ky_Arab_CN
- 0x6B794B474379726Cllu, // ky_Cyrl_KG
- 0x6B7954524C61746Ellu, // ky_Latn_TR
- 0x6C6156414C61746Ellu, // la_Latn_VA
- 0x840B47524C696E61llu, // lab_Lina_GR
- 0x8C0B494C48656272llu, // lad_Hebr_IL
- 0x980B545A4C61746Ellu, // lag_Latn_TZ
- 0x9C0B504B41726162llu, // lah_Arab_PK
- 0xA40B55474C61746Ellu, // laj_Latn_UG
- 0x6C624C554C61746Ellu, // lb_Latn_LU
- 0x902B52554379726Cllu, // lbe_Cyrl_RU
- 0xD82B49444C61746Ellu, // lbw_Latn_ID
- 0xBC4B434E54686169llu, // lcp_Thai_CN
- 0xBC8B494E4C657063llu, // lep_Lepc_IN
- 0xE48B52554379726Cllu, // lez_Cyrl_RU
- 0x6C6755474C61746Ellu, // lg_Latn_UG
- 0x6C694E4C4C61746Ellu, // li_Latn_NL
- 0x950B4E5044657661llu, // lif_Deva_NP
- 0x950B494E4C696D62llu, // lif_Limb_IN
- 0xA50B49544C61746Ellu, // lij_Latn_IT
- 0xC90B434E4C697375llu, // lis_Lisu_CN
- 0xBD2B49444C61746Ellu, // ljp_Latn_ID
- 0xA14B495241726162llu, // lki_Arab_IR
- 0xCD4B55534C61746Ellu, // lkt_Latn_US
- 0xB58B494E54656C75llu, // lmn_Telu_IN
- 0xB98B49544C61746Ellu, // lmo_Latn_IT
- 0x6C6E43444C61746Ellu, // ln_Latn_CD
- 0x6C6F4C414C616F6Fllu, // lo_Laoo_LA
- 0xADCB43444C61746Ellu, // lol_Latn_CD
- 0xE5CB5A4D4C61746Ellu, // loz_Latn_ZM
- 0x8A2B495241726162llu, // lrc_Arab_IR
- 0x6C744C544C61746Ellu, // lt_Latn_LT
- 0x9A6B4C564C61746Ellu, // ltg_Latn_LV
- 0x6C7543444C61746Ellu, // lu_Latn_CD
- 0x828B43444C61746Ellu, // lua_Latn_CD
- 0xBA8B4B454C61746Ellu, // luo_Latn_KE
- 0xE28B4B454C61746Ellu, // luy_Latn_KE
- 0xE68B495241726162llu, // luz_Arab_IR
- 0x6C764C564C61746Ellu, // lv_Latn_LV
- 0xAECB544854686169llu, // lwl_Thai_TH
- 0x9F2B434E48616E73llu, // lzh_Hans_CN
- 0xE72B54524C61746Ellu, // lzz_Latn_TR
- 0x8C0C49444C61746Ellu, // mad_Latn_ID
- 0x940C434D4C61746Ellu, // maf_Latn_CM
- 0x980C494E44657661llu, // mag_Deva_IN
- 0xA00C494E44657661llu, // mai_Deva_IN
- 0xA80C49444C61746Ellu, // mak_Latn_ID
- 0xB40C474D4C61746Ellu, // man_Latn_GM
- 0xB40C474E4E6B6F6Fllu, // man_Nkoo_GN
- 0xC80C4B454C61746Ellu, // mas_Latn_KE
- 0xE40C4D584C61746Ellu, // maz_Latn_MX
- 0x946C52554379726Cllu, // mdf_Cyrl_RU
- 0x9C6C50484C61746Ellu, // mdh_Latn_PH
- 0xC46C49444C61746Ellu, // mdr_Latn_ID
- 0xB48C534C4C61746Ellu, // men_Latn_SL
- 0xC48C4B454C61746Ellu, // mer_Latn_KE
- 0x80AC544841726162llu, // mfa_Arab_TH
- 0x90AC4D554C61746Ellu, // mfe_Latn_MU
- 0x6D674D474C61746Ellu, // mg_Latn_MG
- 0x9CCC4D5A4C61746Ellu, // mgh_Latn_MZ
- 0xB8CC434D4C61746Ellu, // mgo_Latn_CM
- 0xBCCC4E5044657661llu, // mgp_Deva_NP
- 0xE0CC545A4C61746Ellu, // mgy_Latn_TZ
- 0x6D684D484C61746Ellu, // mh_Latn_MH
- 0x6D694E5A4C61746Ellu, // mi_Latn_NZ
- 0xB50C49444C61746Ellu, // min_Latn_ID
- 0xC90C495148617472llu, // mis_Hatr_IQ
- 0x6D6B4D4B4379726Cllu, // mk_Cyrl_MK
- 0x6D6C494E4D6C796Dllu, // ml_Mlym_IN
- 0xC96C53444C61746Ellu, // mls_Latn_SD
- 0x6D6E4D4E4379726Cllu, // mn_Cyrl_MN
- 0x6D6E434E4D6F6E67llu, // mn_Mong_CN
- 0xA1AC494E42656E67llu, // mni_Beng_IN
- 0xD9AC4D4D4D796D72llu, // mnw_Mymr_MM
- 0x91CC43414C61746Ellu, // moe_Latn_CA
- 0x9DCC43414C61746Ellu, // moh_Latn_CA
- 0xC9CC42464C61746Ellu, // mos_Latn_BF
- 0x6D72494E44657661llu, // mr_Deva_IN
- 0x8E2C4E5044657661llu, // mrd_Deva_NP
- 0xA62C52554379726Cllu, // mrj_Cyrl_RU
- 0xBA2C42444D726F6Fllu, // mro_Mroo_BD
- 0x6D734D594C61746Ellu, // ms_Latn_MY
- 0x6D744D544C61746Ellu, // mt_Latn_MT
- 0xC66C494E44657661llu, // mtr_Deva_IN
- 0x828C434D4C61746Ellu, // mua_Latn_CM
- 0xCA8C55534C61746Ellu, // mus_Latn_US
- 0xE2AC504B41726162llu, // mvy_Arab_PK
- 0xAACC4D4C4C61746Ellu, // mwk_Latn_ML
- 0xC6CC494E44657661llu, // mwr_Deva_IN
- 0xD6CC49444C61746Ellu, // mwv_Latn_ID
- 0x8AEC5A574C61746Ellu, // mxc_Latn_ZW
- 0x6D794D4D4D796D72llu, // my_Mymr_MM
- 0xD70C52554379726Cllu, // myv_Cyrl_RU
- 0xDF0C55474C61746Ellu, // myx_Latn_UG
- 0xE70C49524D616E64llu, // myz_Mand_IR
- 0xB72C495241726162llu, // mzn_Arab_IR
- 0x6E614E524C61746Ellu, // na_Latn_NR
- 0xB40D434E48616E73llu, // nan_Hans_CN
- 0xBC0D49544C61746Ellu, // nap_Latn_IT
- 0xC00D4E414C61746Ellu, // naq_Latn_NA
- 0x6E624E4F4C61746Ellu, // nb_Latn_NO
- 0x9C4D4D584C61746Ellu, // nch_Latn_MX
- 0x6E645A574C61746Ellu, // nd_Latn_ZW
- 0x886D4D5A4C61746Ellu, // ndc_Latn_MZ
- 0xC86D44454C61746Ellu, // nds_Latn_DE
- 0x6E654E5044657661llu, // ne_Deva_NP
- 0xD88D4E5044657661llu, // new_Deva_NP
- 0x6E674E414C61746Ellu, // ng_Latn_NA
- 0xACCD4D5A4C61746Ellu, // ngl_Latn_MZ
- 0x90ED4D584C61746Ellu, // nhe_Latn_MX
- 0xD8ED4D584C61746Ellu, // nhw_Latn_MX
- 0xA50D49444C61746Ellu, // nij_Latn_ID
- 0xD10D4E554C61746Ellu, // niu_Latn_NU
- 0xB92D494E4C61746Ellu, // njo_Latn_IN
- 0x6E6C4E4C4C61746Ellu, // nl_Latn_NL
- 0x998D434D4C61746Ellu, // nmg_Latn_CM
- 0x6E6E4E4F4C61746Ellu, // nn_Latn_NO
- 0x9DAD434D4C61746Ellu, // nnh_Latn_CM
- 0x6E6F4E4F4C61746Ellu, // no_Latn_NO
- 0x8DCD54484C616E61llu, // nod_Lana_TH
- 0x91CD494E44657661llu, // noe_Deva_IN
- 0xB5CD534552756E72llu, // non_Runr_SE
- 0xBA0D474E4E6B6F6Fllu, // nqo_Nkoo_GN
- 0x6E725A414C61746Ellu, // nr_Latn_ZA
- 0xAA4D434143616E73llu, // nsk_Cans_CA
- 0xBA4D5A414C61746Ellu, // nso_Latn_ZA
- 0xCA8D53534C61746Ellu, // nus_Latn_SS
- 0x6E7655534C61746Ellu, // nv_Latn_US
- 0xC2ED434E4C61746Ellu, // nxq_Latn_CN
- 0x6E794D574C61746Ellu, // ny_Latn_MW
- 0xB30D545A4C61746Ellu, // nym_Latn_TZ
- 0xB70D55474C61746Ellu, // nyn_Latn_UG
- 0xA32D47484C61746Ellu, // nzi_Latn_GH
- 0x6F6346524C61746Ellu, // oc_Latn_FR
- 0x6F6D45544C61746Ellu, // om_Latn_ET
- 0x6F72494E4F727961llu, // or_Orya_IN
- 0x6F7347454379726Cllu, // os_Cyrl_GE
- 0x824E55534F736765llu, // osa_Osge_US
- 0xAA6E4D4E4F726B68llu, // otk_Orkh_MN
- 0x7061504B41726162llu, // pa_Arab_PK
- 0x7061494E47757275llu, // pa_Guru_IN
- 0x980F50484C61746Ellu, // pag_Latn_PH
- 0xAC0F495250686C69llu, // pal_Phli_IR
- 0xAC0F434E50686C70llu, // pal_Phlp_CN
- 0xB00F50484C61746Ellu, // pam_Latn_PH
- 0xBC0F41574C61746Ellu, // pap_Latn_AW
- 0xD00F50574C61746Ellu, // pau_Latn_PW
- 0x8C4F46524C61746Ellu, // pcd_Latn_FR
- 0xB04F4E474C61746Ellu, // pcm_Latn_NG
- 0x886F55534C61746Ellu, // pdc_Latn_US
- 0xCC6F43414C61746Ellu, // pdt_Latn_CA
- 0xB88F49525870656Fllu, // peo_Xpeo_IR
- 0xACAF44454C61746Ellu, // pfl_Latn_DE
- 0xB4EF4C4250686E78llu, // phn_Phnx_LB
- 0x814F494E42726168llu, // pka_Brah_IN
- 0xB94F4B454C61746Ellu, // pko_Latn_KE
- 0x706C504C4C61746Ellu, // pl_Latn_PL
- 0xC98F49544C61746Ellu, // pms_Latn_IT
- 0xCDAF47524772656Bllu, // pnt_Grek_GR
- 0xB5CF464D4C61746Ellu, // pon_Latn_FM
- 0x822F504B4B686172llu, // pra_Khar_PK
- 0x8E2F495241726162llu, // prd_Arab_IR
- 0x7073414641726162llu, // ps_Arab_AF
- 0x707442524C61746Ellu, // pt_Latn_BR
- 0xD28F47414C61746Ellu, // puu_Latn_GA
- 0x717550454C61746Ellu, // qu_Latn_PE
- 0x8A9047544C61746Ellu, // quc_Latn_GT
- 0x9A9045434C61746Ellu, // qug_Latn_EC
- 0xA411494E44657661llu, // raj_Deva_IN
- 0x945152454C61746Ellu, // rcf_Latn_RE
- 0xA49149444C61746Ellu, // rej_Latn_ID
- 0xB4D149544C61746Ellu, // rgn_Latn_IT
- 0x8111494E4C61746Ellu, // ria_Latn_IN
- 0x95114D4154666E67llu, // rif_Tfng_MA
- 0xC9314E5044657661llu, // rjs_Deva_NP
- 0xCD51424442656E67llu, // rkt_Beng_BD
- 0x726D43484C61746Ellu, // rm_Latn_CH
- 0x959146494C61746Ellu, // rmf_Latn_FI
- 0xB99143484C61746Ellu, // rmo_Latn_CH
- 0xCD91495241726162llu, // rmt_Arab_IR
- 0xD19153454C61746Ellu, // rmu_Latn_SE
- 0x726E42494C61746Ellu, // rn_Latn_BI
- 0x99B14D5A4C61746Ellu, // rng_Latn_MZ
- 0x726F524F4C61746Ellu, // ro_Latn_RO
- 0x85D149444C61746Ellu, // rob_Latn_ID
- 0x95D1545A4C61746Ellu, // rof_Latn_TZ
- 0xB271464A4C61746Ellu, // rtm_Latn_FJ
- 0x727552554379726Cllu, // ru_Cyrl_RU
- 0x929155414379726Cllu, // rue_Cyrl_UA
- 0x9A9153424C61746Ellu, // rug_Latn_SB
- 0x727752574C61746Ellu, // rw_Latn_RW
- 0xAAD1545A4C61746Ellu, // rwk_Latn_TZ
- 0xD3114A504B616E61llu, // ryu_Kana_JP
- 0x7361494E44657661llu, // sa_Deva_IN
- 0x941247484C61746Ellu, // saf_Latn_GH
- 0x9C1252554379726Cllu, // sah_Cyrl_RU
- 0xC0124B454C61746Ellu, // saq_Latn_KE
- 0xC81249444C61746Ellu, // sas_Latn_ID
- 0xCC12494E4C61746Ellu, // sat_Latn_IN
- 0xE412494E53617572llu, // saz_Saur_IN
- 0xBC32545A4C61746Ellu, // sbp_Latn_TZ
- 0x736349544C61746Ellu, // sc_Latn_IT
- 0xA852494E44657661llu, // sck_Deva_IN
- 0xB45249544C61746Ellu, // scn_Latn_IT
- 0xB85247424C61746Ellu, // sco_Latn_GB
- 0xC85243414C61746Ellu, // scs_Latn_CA
- 0x7364504B41726162llu, // sd_Arab_PK
- 0x7364494E44657661llu, // sd_Deva_IN
- 0x7364494E4B686F6Allu, // sd_Khoj_IN
- 0x7364494E53696E64llu, // sd_Sind_IN
- 0x887249544C61746Ellu, // sdc_Latn_IT
- 0x9C72495241726162llu, // sdh_Arab_IR
- 0x73654E4F4C61746Ellu, // se_Latn_NO
- 0x949243494C61746Ellu, // sef_Latn_CI
- 0x9C924D5A4C61746Ellu, // seh_Latn_MZ
- 0xA0924D584C61746Ellu, // sei_Latn_MX
- 0xC8924D4C4C61746Ellu, // ses_Latn_ML
- 0x736743464C61746Ellu, // sg_Latn_CF
- 0x80D249454F67616Dllu, // sga_Ogam_IE
- 0xC8D24C544C61746Ellu, // sgs_Latn_LT
- 0xA0F24D4154666E67llu, // shi_Tfng_MA
- 0xB4F24D4D4D796D72llu, // shn_Mymr_MM
- 0x73694C4B53696E68llu, // si_Sinh_LK
- 0x8D1245544C61746Ellu, // sid_Latn_ET
- 0x736B534B4C61746Ellu, // sk_Latn_SK
- 0xC552504B41726162llu, // skr_Arab_PK
- 0x736C53494C61746Ellu, // sl_Latn_SI
- 0xA172504C4C61746Ellu, // sli_Latn_PL
- 0xE17249444C61746Ellu, // sly_Latn_ID
- 0x736D57534C61746Ellu, // sm_Latn_WS
- 0x819253454C61746Ellu, // sma_Latn_SE
- 0xA59253454C61746Ellu, // smj_Latn_SE
- 0xB59246494C61746Ellu, // smn_Latn_FI
- 0xBD92494C53616D72llu, // smp_Samr_IL
- 0xC99246494C61746Ellu, // sms_Latn_FI
- 0x736E5A574C61746Ellu, // sn_Latn_ZW
- 0xA9B24D4C4C61746Ellu, // snk_Latn_ML
- 0x736F534F4C61746Ellu, // so_Latn_SO
- 0xD1D2544854686169llu, // sou_Thai_TH
- 0x7371414C4C61746Ellu, // sq_Latn_AL
- 0x737252534379726Cllu, // sr_Cyrl_RS
- 0x737252534C61746Ellu, // sr_Latn_RS
- 0x8632494E536F7261llu, // srb_Sora_IN
- 0xB63253524C61746Ellu, // srn_Latn_SR
- 0xC632534E4C61746Ellu, // srr_Latn_SN
- 0xDE32494E44657661llu, // srx_Deva_IN
- 0x73735A414C61746Ellu, // ss_Latn_ZA
- 0xE25245524C61746Ellu, // ssy_Latn_ER
- 0x73745A414C61746Ellu, // st_Latn_ZA
- 0xC27244454C61746Ellu, // stq_Latn_DE
- 0x737549444C61746Ellu, // su_Latn_ID
- 0xAA92545A4C61746Ellu, // suk_Latn_TZ
- 0xCA92474E4C61746Ellu, // sus_Latn_GN
- 0x737653454C61746Ellu, // sv_Latn_SE
- 0x7377545A4C61746Ellu, // sw_Latn_TZ
- 0x86D2595441726162llu, // swb_Arab_YT
- 0x8AD243444C61746Ellu, // swc_Latn_CD
- 0x9AD244454C61746Ellu, // swg_Latn_DE
- 0xD6D2494E44657661llu, // swv_Deva_IN
- 0xB6F249444C61746Ellu, // sxn_Latn_ID
- 0xAF12424442656E67llu, // syl_Beng_BD
- 0xC712495153797263llu, // syr_Syrc_IQ
- 0xAF32504C4C61746Ellu, // szl_Latn_PL
- 0x7461494E54616D6Cllu, // ta_Taml_IN
- 0xA4134E5044657661llu, // taj_Deva_NP
- 0xD83350484C61746Ellu, // tbw_Latn_PH
- 0xE053494E4B6E6461llu, // tcy_Knda_IN
- 0x8C73434E54616C65llu, // tdd_Tale_CN
- 0x98734E5044657661llu, // tdg_Deva_NP
- 0x9C734E5044657661llu, // tdh_Deva_NP
- 0x7465494E54656C75llu, // te_Telu_IN
- 0xB093534C4C61746Ellu, // tem_Latn_SL
- 0xB89355474C61746Ellu, // teo_Latn_UG
- 0xCC93544C4C61746Ellu, // tet_Latn_TL
- 0x7467504B41726162llu, // tg_Arab_PK
- 0x7467544A4379726Cllu, // tg_Cyrl_TJ
- 0x7468544854686169llu, // th_Thai_TH
- 0xACF34E5044657661llu, // thl_Deva_NP
- 0xC0F34E5044657661llu, // thq_Deva_NP
- 0xC4F34E5044657661llu, // thr_Deva_NP
- 0x7469455445746869llu, // ti_Ethi_ET
- 0x9913455245746869llu, // tig_Ethi_ER
- 0xD5134E474C61746Ellu, // tiv_Latn_NG
- 0x746B544D4C61746Ellu, // tk_Latn_TM
- 0xAD53544B4C61746Ellu, // tkl_Latn_TK
- 0xC553415A4C61746Ellu, // tkr_Latn_AZ
- 0xCD534E5044657661llu, // tkt_Deva_NP
- 0x746C50484C61746Ellu, // tl_Latn_PH
- 0xE173415A4C61746Ellu, // tly_Latn_AZ
- 0x9D934E454C61746Ellu, // tmh_Latn_NE
- 0x746E5A414C61746Ellu, // tn_Latn_ZA
- 0x746F544F4C61746Ellu, // to_Latn_TO
- 0x99D34D574C61746Ellu, // tog_Latn_MW
- 0xA1F350474C61746Ellu, // tpi_Latn_PG
- 0x747254524C61746Ellu, // tr_Latn_TR
- 0xD23354524C61746Ellu, // tru_Latn_TR
- 0xD63354574C61746Ellu, // trv_Latn_TW
- 0x74735A414C61746Ellu, // ts_Latn_ZA
- 0x8E5347524772656Bllu, // tsd_Grek_GR
- 0x96534E5044657661llu, // tsf_Deva_NP
- 0x9A5350484C61746Ellu, // tsg_Latn_PH
- 0xA653425454696274llu, // tsj_Tibt_BT
- 0x747452554379726Cllu, // tt_Cyrl_RU
- 0xA67355474C61746Ellu, // ttj_Latn_UG
- 0xCA73544854686169llu, // tts_Thai_TH
- 0xCE73415A4C61746Ellu, // ttt_Latn_AZ
- 0xB2934D574C61746Ellu, // tum_Latn_MW
- 0xAEB354564C61746Ellu, // tvl_Latn_TV
- 0xC2D34E454C61746Ellu, // twq_Latn_NE
- 0x9AF3434E54616E67llu, // txg_Tang_CN
- 0x747950464C61746Ellu, // ty_Latn_PF
- 0xD71352554379726Cllu, // tyv_Cyrl_RU
- 0xB3334D414C61746Ellu, // tzm_Latn_MA
- 0xB07452554379726Cllu, // udm_Cyrl_RU
- 0x7567434E41726162llu, // ug_Arab_CN
- 0x75674B5A4379726Cllu, // ug_Cyrl_KZ
- 0x80D4535955676172llu, // uga_Ugar_SY
- 0x756B55414379726Cllu, // uk_Cyrl_UA
- 0xA174464D4C61746Ellu, // uli_Latn_FM
- 0x8594414F4C61746Ellu, // umb_Latn_AO
- 0xC5B4494E42656E67llu, // unr_Beng_IN
- 0xC5B44E5044657661llu, // unr_Deva_NP
- 0xDDB4494E42656E67llu, // unx_Beng_IN
- 0x7572504B41726162llu, // ur_Arab_PK
- 0x757A414641726162llu, // uz_Arab_AF
- 0x757A555A4C61746Ellu, // uz_Latn_UZ
- 0xA0154C5256616969llu, // vai_Vaii_LR
- 0x76655A414C61746Ellu, // ve_Latn_ZA
- 0x889549544C61746Ellu, // vec_Latn_IT
- 0xBC9552554C61746Ellu, // vep_Latn_RU
- 0x7669564E4C61746Ellu, // vi_Latn_VN
- 0x891553584C61746Ellu, // vic_Latn_SX
- 0xC97542454C61746Ellu, // vls_Latn_BE
- 0x959544454C61746Ellu, // vmf_Latn_DE
- 0xD9954D5A4C61746Ellu, // vmw_Latn_MZ
- 0xCDD552554C61746Ellu, // vot_Latn_RU
- 0xBA3545454C61746Ellu, // vro_Latn_EE
- 0xB695545A4C61746Ellu, // vun_Latn_TZ
- 0x776142454C61746Ellu, // wa_Latn_BE
- 0x901643484C61746Ellu, // wae_Latn_CH
- 0xAC16455445746869llu, // wal_Ethi_ET
- 0xC41650484C61746Ellu, // war_Latn_PH
- 0xBC3641554C61746Ellu, // wbp_Latn_AU
- 0xC036494E54656C75llu, // wbq_Telu_IN
- 0xC436494E44657661llu, // wbr_Deva_IN
- 0xC97657464C61746Ellu, // wls_Latn_WF
- 0xA1B64B4D41726162llu, // wni_Arab_KM
- 0x776F534E4C61746Ellu, // wo_Latn_SN
- 0xB276494E44657661llu, // wtm_Deva_IN
- 0xD296434E48616E73llu, // wuu_Hans_CN
- 0xD41742524C61746Ellu, // xav_Latn_BR
- 0xC457545243617269llu, // xcr_Cari_TR
- 0x78685A414C61746Ellu, // xh_Latn_ZA
- 0x897754524C796369llu, // xlc_Lyci_TR
- 0x8D7754524C796469llu, // xld_Lydi_TR
- 0x9597474547656F72llu, // xmf_Geor_GE
- 0xB597434E4D616E69llu, // xmn_Mani_CN
- 0xC59753444D657263llu, // xmr_Merc_SD
- 0x81B753414E617262llu, // xna_Narb_SA
- 0xC5B7494E44657661llu, // xnr_Deva_IN
- 0x99D755474C61746Ellu, // xog_Latn_UG
- 0xC5F7495250727469llu, // xpr_Prti_IR
- 0x8257594553617262llu, // xsa_Sarb_YE
- 0xC6574E5044657661llu, // xsr_Deva_NP
- 0xB8184D5A4C61746Ellu, // yao_Latn_MZ
- 0xBC18464D4C61746Ellu, // yap_Latn_FM
- 0xD418434D4C61746Ellu, // yav_Latn_CM
- 0x8438434D4C61746Ellu, // ybb_Latn_CM
- 0x796F4E474C61746Ellu, // yo_Latn_NG
- 0xAE3842524C61746Ellu, // yrl_Latn_BR
- 0x82984D584C61746Ellu, // yua_Latn_MX
- 0x9298434E48616E73llu, // yue_Hans_CN
- 0x9298484B48616E74llu, // yue_Hant_HK
- 0x7A61434E4C61746Ellu, // za_Latn_CN
- 0x981953444C61746Ellu, // zag_Latn_SD
- 0xA4794B4D41726162llu, // zdj_Arab_KM
- 0x80994E4C4C61746Ellu, // zea_Latn_NL
- 0x9CD94D4154666E67llu, // zgh_Tfng_MA
- 0x7A685457426F706Fllu, // zh_Bopo_TW
- 0x7A68545748616E62llu, // zh_Hanb_TW
- 0x7A68434E48616E73llu, // zh_Hans_CN
- 0x7A68545748616E74llu, // zh_Hant_TW
- 0xB17954474C61746Ellu, // zlm_Latn_TG
- 0xA1994D594C61746Ellu, // zmi_Latn_MY
- 0x7A755A414C61746Ellu, // zu_Latn_ZA
- 0x833954524C61746Ellu, // zza_Latn_TR
+ 0x616145544C61746ELLU, // aa_Latn_ET
+ 0x616247454379726CLLU, // ab_Cyrl_GE
+ 0xC42047484C61746ELLU, // abr_Latn_GH
+ 0x904049444C61746ELLU, // ace_Latn_ID
+ 0x9C4055474C61746ELLU, // ach_Latn_UG
+ 0x806047484C61746ELLU, // ada_Latn_GH
+ 0xE06052554379726CLLU, // ady_Cyrl_RU
+ 0x6165495241767374LLU, // ae_Avst_IR
+ 0x8480544E41726162LLU, // aeb_Arab_TN
+ 0x61665A414C61746ELLU, // af_Latn_ZA
+ 0xC0C0434D4C61746ELLU, // agq_Latn_CM
+ 0xB8E0494E41686F6DLLU, // aho_Ahom_IN
+ 0x616B47484C61746ELLU, // ak_Latn_GH
+ 0xA940495158737578LLU, // akk_Xsux_IQ
+ 0xB560584B4C61746ELLU, // aln_Latn_XK
+ 0xCD6052554379726CLLU, // alt_Cyrl_RU
+ 0x616D455445746869LLU, // am_Ethi_ET
+ 0xB9804E474C61746ELLU, // amo_Latn_NG
+ 0xE5C049444C61746ELLU, // aoz_Latn_ID
+ 0x8DE0544741726162LLU, // apd_Arab_TG
+ 0x6172454741726162LLU, // ar_Arab_EG
+ 0x8A20495241726D69LLU, // arc_Armi_IR
+ 0x8A204A4F4E626174LLU, // arc_Nbat_JO
+ 0x8A20535950616C6DLLU, // arc_Palm_SY
+ 0xB620434C4C61746ELLU, // arn_Latn_CL
+ 0xBA20424F4C61746ELLU, // aro_Latn_BO
+ 0xC220445A41726162LLU, // arq_Arab_DZ
+ 0xE2204D4141726162LLU, // ary_Arab_MA
+ 0xE620454741726162LLU, // arz_Arab_EG
+ 0x6173494E42656E67LLU, // as_Beng_IN
+ 0x8240545A4C61746ELLU, // asa_Latn_TZ
+ 0x9240555353676E77LLU, // ase_Sgnw_US
+ 0xCE4045534C61746ELLU, // ast_Latn_ES
+ 0xA66043414C61746ELLU, // atj_Latn_CA
+ 0x617652554379726CLLU, // av_Cyrl_RU
+ 0x82C0494E44657661LLU, // awa_Deva_IN
+ 0x6179424F4C61746ELLU, // ay_Latn_BO
+ 0x617A495241726162LLU, // az_Arab_IR
+ 0x617A415A4C61746ELLU, // az_Latn_AZ
+ 0x626152554379726CLLU, // ba_Cyrl_RU
+ 0xAC01504B41726162LLU, // bal_Arab_PK
+ 0xB40149444C61746ELLU, // ban_Latn_ID
+ 0xBC014E5044657661LLU, // bap_Deva_NP
+ 0xC40141544C61746ELLU, // bar_Latn_AT
+ 0xC801434D4C61746ELLU, // bas_Latn_CM
+ 0xDC01434D42616D75LLU, // bax_Bamu_CM
+ 0x882149444C61746ELLU, // bbc_Latn_ID
+ 0xA421434D4C61746ELLU, // bbj_Latn_CM
+ 0xA04143494C61746ELLU, // bci_Latn_CI
+ 0x626542594379726CLLU, // be_Cyrl_BY
+ 0xA481534441726162LLU, // bej_Arab_SD
+ 0xB0815A4D4C61746ELLU, // bem_Latn_ZM
+ 0xD88149444C61746ELLU, // bew_Latn_ID
+ 0xE481545A4C61746ELLU, // bez_Latn_TZ
+ 0x8CA1434D4C61746ELLU, // bfd_Latn_CM
+ 0xC0A1494E54616D6CLLU, // bfq_Taml_IN
+ 0xCCA1504B41726162LLU, // bft_Arab_PK
+ 0xE0A1494E44657661LLU, // bfy_Deva_IN
+ 0x626742474379726CLLU, // bg_Cyrl_BG
+ 0x88C1494E44657661LLU, // bgc_Deva_IN
+ 0xB4C1504B41726162LLU, // bgn_Arab_PK
+ 0xDCC154524772656BLLU, // bgx_Grek_TR
+ 0x84E1494E44657661LLU, // bhb_Deva_IN
+ 0xA0E1494E44657661LLU, // bhi_Deva_IN
+ 0xA8E150484C61746ELLU, // bhk_Latn_PH
+ 0xB8E1494E44657661LLU, // bho_Deva_IN
+ 0x626956554C61746ELLU, // bi_Latn_VU
+ 0xA90150484C61746ELLU, // bik_Latn_PH
+ 0xB5014E474C61746ELLU, // bin_Latn_NG
+ 0xA521494E44657661LLU, // bjj_Deva_IN
+ 0xB52149444C61746ELLU, // bjn_Latn_ID
+ 0xB141434D4C61746ELLU, // bkm_Latn_CM
+ 0xD14150484C61746ELLU, // bku_Latn_PH
+ 0xCD61564E54617674LLU, // blt_Tavt_VN
+ 0x626D4D4C4C61746ELLU, // bm_Latn_ML
+ 0xC1814D4C4C61746ELLU, // bmq_Latn_ML
+ 0x626E424442656E67LLU, // bn_Beng_BD
+ 0x626F434E54696274LLU, // bo_Tibt_CN
+ 0xE1E1494E42656E67LLU, // bpy_Beng_IN
+ 0xA201495241726162LLU, // bqi_Arab_IR
+ 0xD60143494C61746ELLU, // bqv_Latn_CI
+ 0x627246524C61746ELLU, // br_Latn_FR
+ 0x8221494E44657661LLU, // bra_Deva_IN
+ 0x9E21504B41726162LLU, // brh_Arab_PK
+ 0xDE21494E44657661LLU, // brx_Deva_IN
+ 0x627342414C61746ELLU, // bs_Latn_BA
+ 0xC2414C5242617373LLU, // bsq_Bass_LR
+ 0xCA41434D4C61746ELLU, // bss_Latn_CM
+ 0xBA6150484C61746ELLU, // bto_Latn_PH
+ 0xD661504B44657661LLU, // btv_Deva_PK
+ 0x828152554379726CLLU, // bua_Cyrl_RU
+ 0x8A8159544C61746ELLU, // buc_Latn_YT
+ 0x9A8149444C61746ELLU, // bug_Latn_ID
+ 0xB281434D4C61746ELLU, // bum_Latn_CM
+ 0x86A147514C61746ELLU, // bvb_Latn_GQ
+ 0xB701455245746869LLU, // byn_Ethi_ER
+ 0xD701434D4C61746ELLU, // byv_Latn_CM
+ 0x93214D4C4C61746ELLU, // bze_Latn_ML
+ 0x636145534C61746ELLU, // ca_Latn_ES
+ 0x9C424E474C61746ELLU, // cch_Latn_NG
+ 0xBC42494E42656E67LLU, // ccp_Beng_IN
+ 0xBC42424443616B6DLLU, // ccp_Cakm_BD
+ 0x636552554379726CLLU, // ce_Cyrl_RU
+ 0x848250484C61746ELLU, // ceb_Latn_PH
+ 0x98C255474C61746ELLU, // cgg_Latn_UG
+ 0x636847554C61746ELLU, // ch_Latn_GU
+ 0xA8E2464D4C61746ELLU, // chk_Latn_FM
+ 0xB0E252554379726CLLU, // chm_Cyrl_RU
+ 0xB8E255534C61746ELLU, // cho_Latn_US
+ 0xBCE243414C61746ELLU, // chp_Latn_CA
+ 0xC4E2555343686572LLU, // chr_Cher_US
+ 0x81224B4841726162LLU, // cja_Arab_KH
+ 0xB122564E4368616DLLU, // cjm_Cham_VN
+ 0x8542495141726162LLU, // ckb_Arab_IQ
+ 0x636F46524C61746ELLU, // co_Latn_FR
+ 0xBDC24547436F7074LLU, // cop_Copt_EG
+ 0xC9E250484C61746ELLU, // cps_Latn_PH
+ 0x6372434143616E73LLU, // cr_Cans_CA
+ 0xA622434143616E73LLU, // crj_Cans_CA
+ 0xAA22434143616E73LLU, // crk_Cans_CA
+ 0xAE22434143616E73LLU, // crl_Cans_CA
+ 0xB222434143616E73LLU, // crm_Cans_CA
+ 0xCA2253434C61746ELLU, // crs_Latn_SC
+ 0x6373435A4C61746ELLU, // cs_Latn_CZ
+ 0x8642504C4C61746ELLU, // csb_Latn_PL
+ 0xDA42434143616E73LLU, // csw_Cans_CA
+ 0x8E624D4D50617563LLU, // ctd_Pauc_MM
+ 0x637552554379726CLLU, // cu_Cyrl_RU
+ 0x63754247476C6167LLU, // cu_Glag_BG
+ 0x637652554379726CLLU, // cv_Cyrl_RU
+ 0x637947424C61746ELLU, // cy_Latn_GB
+ 0x6461444B4C61746ELLU, // da_Latn_DK
+ 0xA80355534C61746ELLU, // dak_Latn_US
+ 0xC40352554379726CLLU, // dar_Cyrl_RU
+ 0xD4034B454C61746ELLU, // dav_Latn_KE
+ 0x8843494E41726162LLU, // dcc_Arab_IN
+ 0x646544454C61746ELLU, // de_Latn_DE
+ 0xB48343414C61746ELLU, // den_Latn_CA
+ 0xC4C343414C61746ELLU, // dgr_Latn_CA
+ 0x91234E454C61746ELLU, // dje_Latn_NE
+ 0xA5A343494C61746ELLU, // dnj_Latn_CI
+ 0xA1C3494E41726162LLU, // doi_Arab_IN
+ 0x864344454C61746ELLU, // dsb_Latn_DE
+ 0xB2634D4C4C61746ELLU, // dtm_Latn_ML
+ 0xBE634D594C61746ELLU, // dtp_Latn_MY
+ 0xE2634E5044657661LLU, // dty_Deva_NP
+ 0x8283434D4C61746ELLU, // dua_Latn_CM
+ 0x64764D5654686161LLU, // dv_Thaa_MV
+ 0xBB03534E4C61746ELLU, // dyo_Latn_SN
+ 0xD30342464C61746ELLU, // dyu_Latn_BF
+ 0x647A425454696274LLU, // dz_Tibt_BT
+ 0xD0244B454C61746ELLU, // ebu_Latn_KE
+ 0x656547484C61746ELLU, // ee_Latn_GH
+ 0xA0A44E474C61746ELLU, // efi_Latn_NG
+ 0xACC449544C61746ELLU, // egl_Latn_IT
+ 0xE0C4454745677970LLU, // egy_Egyp_EG
+ 0xE1444D4D4B616C69LLU, // eky_Kali_MM
+ 0x656C47524772656BLLU, // el_Grek_GR
+ 0x656E47424C61746ELLU, // en_Latn_GB
+ 0x656E55534C61746ELLU, // en_Latn_US
+ 0x656E474253686177LLU, // en_Shaw_GB
+ 0x657345534C61746ELLU, // es_Latn_ES
+ 0x65734D584C61746ELLU, // es_Latn_MX
+ 0x657355534C61746ELLU, // es_Latn_US
+ 0xD24455534C61746ELLU, // esu_Latn_US
+ 0x657445454C61746ELLU, // et_Latn_EE
+ 0xCE6449544974616CLLU, // ett_Ital_IT
+ 0x657545534C61746ELLU, // eu_Latn_ES
+ 0xBAC4434D4C61746ELLU, // ewo_Latn_CM
+ 0xCEE445534C61746ELLU, // ext_Latn_ES
+ 0x6661495241726162LLU, // fa_Arab_IR
+ 0xB40547514C61746ELLU, // fan_Latn_GQ
+ 0x6666474E41646C6DLLU, // ff_Adlm_GN
+ 0x6666534E4C61746ELLU, // ff_Latn_SN
+ 0xB0A54D4C4C61746ELLU, // ffm_Latn_ML
+ 0x666946494C61746ELLU, // fi_Latn_FI
+ 0x8105534441726162LLU, // fia_Arab_SD
+ 0xAD0550484C61746ELLU, // fil_Latn_PH
+ 0xCD0553454C61746ELLU, // fit_Latn_SE
+ 0x666A464A4C61746ELLU, // fj_Latn_FJ
+ 0x666F464F4C61746ELLU, // fo_Latn_FO
+ 0xB5C5424A4C61746ELLU, // fon_Latn_BJ
+ 0x667246524C61746ELLU, // fr_Latn_FR
+ 0x8A2555534C61746ELLU, // frc_Latn_US
+ 0xBE2546524C61746ELLU, // frp_Latn_FR
+ 0xC62544454C61746ELLU, // frr_Latn_DE
+ 0xCA2544454C61746ELLU, // frs_Latn_DE
+ 0x8685434D41726162LLU, // fub_Arab_CM
+ 0x8E8557464C61746ELLU, // fud_Latn_WF
+ 0x9685474E4C61746ELLU, // fuf_Latn_GN
+ 0xC2854E454C61746ELLU, // fuq_Latn_NE
+ 0xC68549544C61746ELLU, // fur_Latn_IT
+ 0xD6854E474C61746ELLU, // fuv_Latn_NG
+ 0xC6A553444C61746ELLU, // fvr_Latn_SD
+ 0x66794E4C4C61746ELLU, // fy_Latn_NL
+ 0x676149454C61746ELLU, // ga_Latn_IE
+ 0x800647484C61746ELLU, // gaa_Latn_GH
+ 0x98064D444C61746ELLU, // gag_Latn_MD
+ 0xB406434E48616E73LLU, // gan_Hans_CN
+ 0xE00649444C61746ELLU, // gay_Latn_ID
+ 0xB026494E44657661LLU, // gbm_Deva_IN
+ 0xE426495241726162LLU, // gbz_Arab_IR
+ 0xC44647464C61746ELLU, // gcr_Latn_GF
+ 0x676447424C61746ELLU, // gd_Latn_GB
+ 0xE486455445746869LLU, // gez_Ethi_ET
+ 0xB4C64E5044657661LLU, // ggn_Deva_NP
+ 0xAD064B494C61746ELLU, // gil_Latn_KI
+ 0xA926504B41726162LLU, // gjk_Arab_PK
+ 0xD126504B41726162LLU, // gju_Arab_PK
+ 0x676C45534C61746ELLU, // gl_Latn_ES
+ 0xA966495241726162LLU, // glk_Arab_IR
+ 0x676E50594C61746ELLU, // gn_Latn_PY
+ 0xB1C6494E44657661LLU, // gom_Deva_IN
+ 0xB5C6494E54656C75LLU, // gon_Telu_IN
+ 0xC5C649444C61746ELLU, // gor_Latn_ID
+ 0xC9C64E4C4C61746ELLU, // gos_Latn_NL
+ 0xCDC65541476F7468LLU, // got_Goth_UA
+ 0x8A26435943707274LLU, // grc_Cprt_CY
+ 0x8A2647524C696E62LLU, // grc_Linb_GR
+ 0xCE26494E42656E67LLU, // grt_Beng_IN
+ 0xDA4643484C61746ELLU, // gsw_Latn_CH
+ 0x6775494E47756A72LLU, // gu_Gujr_IN
+ 0x868642524C61746ELLU, // gub_Latn_BR
+ 0x8A86434F4C61746ELLU, // guc_Latn_CO
+ 0xC68647484C61746ELLU, // gur_Latn_GH
+ 0xE6864B454C61746ELLU, // guz_Latn_KE
+ 0x6776494D4C61746ELLU, // gv_Latn_IM
+ 0xC6A64E5044657661LLU, // gvr_Deva_NP
+ 0xA2C643414C61746ELLU, // gwi_Latn_CA
+ 0x68614E474C61746ELLU, // ha_Latn_NG
+ 0xA807434E48616E73LLU, // hak_Hans_CN
+ 0xD80755534C61746ELLU, // haw_Latn_US
+ 0xE407414641726162LLU, // haz_Arab_AF
+ 0x6865494C48656272LLU, // he_Hebr_IL
+ 0x6869494E44657661LLU, // hi_Deva_IN
+ 0x9507464A4C61746ELLU, // hif_Latn_FJ
+ 0xAD0750484C61746ELLU, // hil_Latn_PH
+ 0xD1675452486C7577LLU, // hlu_Hluw_TR
+ 0x8D87434E506C7264LLU, // hmd_Plrd_CN
+ 0x8DA7504B41726162LLU, // hnd_Arab_PK
+ 0x91A7494E44657661LLU, // hne_Deva_IN
+ 0xA5A74C41486D6E67LLU, // hnj_Hmng_LA
+ 0xB5A750484C61746ELLU, // hnn_Latn_PH
+ 0xB9A7504B41726162LLU, // hno_Arab_PK
+ 0x686F50474C61746ELLU, // ho_Latn_PG
+ 0x89C7494E44657661LLU, // hoc_Deva_IN
+ 0xA5C7494E44657661LLU, // hoj_Deva_IN
+ 0x687248524C61746ELLU, // hr_Latn_HR
+ 0x864744454C61746ELLU, // hsb_Latn_DE
+ 0xB647434E48616E73LLU, // hsn_Hans_CN
+ 0x687448544C61746ELLU, // ht_Latn_HT
+ 0x687548554C61746ELLU, // hu_Latn_HU
+ 0x6879414D41726D6ELLU, // hy_Armn_AM
+ 0x687A4E414C61746ELLU, // hz_Latn_NA
+ 0x696146524C61746ELLU, // ia_Latn_FR
+ 0x80284D594C61746ELLU, // iba_Latn_MY
+ 0x84284E474C61746ELLU, // ibb_Latn_NG
+ 0x696449444C61746ELLU, // id_Latn_ID
+ 0x69674E474C61746ELLU, // ig_Latn_NG
+ 0x6969434E59696969LLU, // ii_Yiii_CN
+ 0x696B55534C61746ELLU, // ik_Latn_US
+ 0xCD4843414C61746ELLU, // ikt_Latn_CA
+ 0xB96850484C61746ELLU, // ilo_Latn_PH
+ 0x696E49444C61746ELLU, // in_Latn_ID
+ 0x9DA852554379726CLLU, // inh_Cyrl_RU
+ 0x697349534C61746ELLU, // is_Latn_IS
+ 0x697449544C61746ELLU, // it_Latn_IT
+ 0x6975434143616E73LLU, // iu_Cans_CA
+ 0x6977494C48656272LLU, // iw_Hebr_IL
+ 0x9F2852554C61746ELLU, // izh_Latn_RU
+ 0x6A614A504A70616ELLU, // ja_Jpan_JP
+ 0xB0094A4D4C61746ELLU, // jam_Latn_JM
+ 0xB8C9434D4C61746ELLU, // jgo_Latn_CM
+ 0x8989545A4C61746ELLU, // jmc_Latn_TZ
+ 0xAD894E5044657661LLU, // jml_Deva_NP
+ 0xCE89444B4C61746ELLU, // jut_Latn_DK
+ 0x6A7649444C61746ELLU, // jv_Latn_ID
+ 0x6A7749444C61746ELLU, // jw_Latn_ID
+ 0x6B61474547656F72LLU, // ka_Geor_GE
+ 0x800A555A4379726CLLU, // kaa_Cyrl_UZ
+ 0x840A445A4C61746ELLU, // kab_Latn_DZ
+ 0x880A4D4D4C61746ELLU, // kac_Latn_MM
+ 0xA40A4E474C61746ELLU, // kaj_Latn_NG
+ 0xB00A4B454C61746ELLU, // kam_Latn_KE
+ 0xB80A4D4C4C61746ELLU, // kao_Latn_ML
+ 0x8C2A52554379726CLLU, // kbd_Cyrl_RU
+ 0xE02A4E4541726162LLU, // kby_Arab_NE
+ 0x984A4E474C61746ELLU, // kcg_Latn_NG
+ 0xA84A5A574C61746ELLU, // kck_Latn_ZW
+ 0x906A545A4C61746ELLU, // kde_Latn_TZ
+ 0x9C6A544741726162LLU, // kdh_Arab_TG
+ 0xCC6A544854686169LLU, // kdt_Thai_TH
+ 0x808A43564C61746ELLU, // kea_Latn_CV
+ 0xB48A434D4C61746ELLU, // ken_Latn_CM
+ 0xB8AA43494C61746ELLU, // kfo_Latn_CI
+ 0xC4AA494E44657661LLU, // kfr_Deva_IN
+ 0xE0AA494E44657661LLU, // kfy_Deva_IN
+ 0x6B6743444C61746ELLU, // kg_Latn_CD
+ 0x90CA49444C61746ELLU, // kge_Latn_ID
+ 0xBCCA42524C61746ELLU, // kgp_Latn_BR
+ 0x80EA494E4C61746ELLU, // kha_Latn_IN
+ 0x84EA434E54616C75LLU, // khb_Talu_CN
+ 0xB4EA494E44657661LLU, // khn_Deva_IN
+ 0xC0EA4D4C4C61746ELLU, // khq_Latn_ML
+ 0xCCEA494E4D796D72LLU, // kht_Mymr_IN
+ 0xD8EA504B41726162LLU, // khw_Arab_PK
+ 0x6B694B454C61746ELLU, // ki_Latn_KE
+ 0xD10A54524C61746ELLU, // kiu_Latn_TR
+ 0x6B6A4E414C61746ELLU, // kj_Latn_NA
+ 0x992A4C414C616F6FLLU, // kjg_Laoo_LA
+ 0x6B6B434E41726162LLU, // kk_Arab_CN
+ 0x6B6B4B5A4379726CLLU, // kk_Cyrl_KZ
+ 0xA54A434D4C61746ELLU, // kkj_Latn_CM
+ 0x6B6C474C4C61746ELLU, // kl_Latn_GL
+ 0xB56A4B454C61746ELLU, // kln_Latn_KE
+ 0x6B6D4B484B686D72LLU, // km_Khmr_KH
+ 0x858A414F4C61746ELLU, // kmb_Latn_AO
+ 0x6B6E494E4B6E6461LLU, // kn_Knda_IN
+ 0x6B6F4B524B6F7265LLU, // ko_Kore_KR
+ 0xA1CA52554379726CLLU, // koi_Cyrl_RU
+ 0xA9CA494E44657661LLU, // kok_Deva_IN
+ 0xC9CA464D4C61746ELLU, // kos_Latn_FM
+ 0x91EA4C524C61746ELLU, // kpe_Latn_LR
+ 0x8A2A52554379726CLLU, // krc_Cyrl_RU
+ 0xA22A534C4C61746ELLU, // kri_Latn_SL
+ 0xA62A50484C61746ELLU, // krj_Latn_PH
+ 0xAE2A52554C61746ELLU, // krl_Latn_RU
+ 0xD22A494E44657661LLU, // kru_Deva_IN
+ 0x6B73494E41726162LLU, // ks_Arab_IN
+ 0x864A545A4C61746ELLU, // ksb_Latn_TZ
+ 0x964A434D4C61746ELLU, // ksf_Latn_CM
+ 0x9E4A44454C61746ELLU, // ksh_Latn_DE
+ 0x6B75495141726162LLU, // ku_Arab_IQ
+ 0x6B7554524C61746ELLU, // ku_Latn_TR
+ 0xB28A52554379726CLLU, // kum_Cyrl_RU
+ 0x6B7652554379726CLLU, // kv_Cyrl_RU
+ 0xC6AA49444C61746ELLU, // kvr_Latn_ID
+ 0xDEAA504B41726162LLU, // kvx_Arab_PK
+ 0x6B7747424C61746ELLU, // kw_Latn_GB
+ 0xB2EA544854686169LLU, // kxm_Thai_TH
+ 0xBEEA504B41726162LLU, // kxp_Arab_PK
+ 0x6B79434E41726162LLU, // ky_Arab_CN
+ 0x6B794B474379726CLLU, // ky_Cyrl_KG
+ 0x6B7954524C61746ELLU, // ky_Latn_TR
+ 0x6C6156414C61746ELLU, // la_Latn_VA
+ 0x840B47524C696E61LLU, // lab_Lina_GR
+ 0x8C0B494C48656272LLU, // lad_Hebr_IL
+ 0x980B545A4C61746ELLU, // lag_Latn_TZ
+ 0x9C0B504B41726162LLU, // lah_Arab_PK
+ 0xA40B55474C61746ELLU, // laj_Latn_UG
+ 0x6C624C554C61746ELLU, // lb_Latn_LU
+ 0x902B52554379726CLLU, // lbe_Cyrl_RU
+ 0xD82B49444C61746ELLU, // lbw_Latn_ID
+ 0xBC4B434E54686169LLU, // lcp_Thai_CN
+ 0xBC8B494E4C657063LLU, // lep_Lepc_IN
+ 0xE48B52554379726CLLU, // lez_Cyrl_RU
+ 0x6C6755474C61746ELLU, // lg_Latn_UG
+ 0x6C694E4C4C61746ELLU, // li_Latn_NL
+ 0x950B4E5044657661LLU, // lif_Deva_NP
+ 0x950B494E4C696D62LLU, // lif_Limb_IN
+ 0xA50B49544C61746ELLU, // lij_Latn_IT
+ 0xC90B434E4C697375LLU, // lis_Lisu_CN
+ 0xBD2B49444C61746ELLU, // ljp_Latn_ID
+ 0xA14B495241726162LLU, // lki_Arab_IR
+ 0xCD4B55534C61746ELLU, // lkt_Latn_US
+ 0xB58B494E54656C75LLU, // lmn_Telu_IN
+ 0xB98B49544C61746ELLU, // lmo_Latn_IT
+ 0x6C6E43444C61746ELLU, // ln_Latn_CD
+ 0x6C6F4C414C616F6FLLU, // lo_Laoo_LA
+ 0xADCB43444C61746ELLU, // lol_Latn_CD
+ 0xE5CB5A4D4C61746ELLU, // loz_Latn_ZM
+ 0x8A2B495241726162LLU, // lrc_Arab_IR
+ 0x6C744C544C61746ELLU, // lt_Latn_LT
+ 0x9A6B4C564C61746ELLU, // ltg_Latn_LV
+ 0x6C7543444C61746ELLU, // lu_Latn_CD
+ 0x828B43444C61746ELLU, // lua_Latn_CD
+ 0xBA8B4B454C61746ELLU, // luo_Latn_KE
+ 0xE28B4B454C61746ELLU, // luy_Latn_KE
+ 0xE68B495241726162LLU, // luz_Arab_IR
+ 0x6C764C564C61746ELLU, // lv_Latn_LV
+ 0xAECB544854686169LLU, // lwl_Thai_TH
+ 0x9F2B434E48616E73LLU, // lzh_Hans_CN
+ 0xE72B54524C61746ELLU, // lzz_Latn_TR
+ 0x8C0C49444C61746ELLU, // mad_Latn_ID
+ 0x940C434D4C61746ELLU, // maf_Latn_CM
+ 0x980C494E44657661LLU, // mag_Deva_IN
+ 0xA00C494E44657661LLU, // mai_Deva_IN
+ 0xA80C49444C61746ELLU, // mak_Latn_ID
+ 0xB40C474D4C61746ELLU, // man_Latn_GM
+ 0xB40C474E4E6B6F6FLLU, // man_Nkoo_GN
+ 0xC80C4B454C61746ELLU, // mas_Latn_KE
+ 0xE40C4D584C61746ELLU, // maz_Latn_MX
+ 0x946C52554379726CLLU, // mdf_Cyrl_RU
+ 0x9C6C50484C61746ELLU, // mdh_Latn_PH
+ 0xC46C49444C61746ELLU, // mdr_Latn_ID
+ 0xB48C534C4C61746ELLU, // men_Latn_SL
+ 0xC48C4B454C61746ELLU, // mer_Latn_KE
+ 0x80AC544841726162LLU, // mfa_Arab_TH
+ 0x90AC4D554C61746ELLU, // mfe_Latn_MU
+ 0x6D674D474C61746ELLU, // mg_Latn_MG
+ 0x9CCC4D5A4C61746ELLU, // mgh_Latn_MZ
+ 0xB8CC434D4C61746ELLU, // mgo_Latn_CM
+ 0xBCCC4E5044657661LLU, // mgp_Deva_NP
+ 0xE0CC545A4C61746ELLU, // mgy_Latn_TZ
+ 0x6D684D484C61746ELLU, // mh_Latn_MH
+ 0x6D694E5A4C61746ELLU, // mi_Latn_NZ
+ 0xB50C49444C61746ELLU, // min_Latn_ID
+ 0xC90C495148617472LLU, // mis_Hatr_IQ
+ 0x6D6B4D4B4379726CLLU, // mk_Cyrl_MK
+ 0x6D6C494E4D6C796DLLU, // ml_Mlym_IN
+ 0xC96C53444C61746ELLU, // mls_Latn_SD
+ 0x6D6E4D4E4379726CLLU, // mn_Cyrl_MN
+ 0x6D6E434E4D6F6E67LLU, // mn_Mong_CN
+ 0xA1AC494E42656E67LLU, // mni_Beng_IN
+ 0xD9AC4D4D4D796D72LLU, // mnw_Mymr_MM
+ 0x91CC43414C61746ELLU, // moe_Latn_CA
+ 0x9DCC43414C61746ELLU, // moh_Latn_CA
+ 0xC9CC42464C61746ELLU, // mos_Latn_BF
+ 0x6D72494E44657661LLU, // mr_Deva_IN
+ 0x8E2C4E5044657661LLU, // mrd_Deva_NP
+ 0xA62C52554379726CLLU, // mrj_Cyrl_RU
+ 0xBA2C42444D726F6FLLU, // mro_Mroo_BD
+ 0x6D734D594C61746ELLU, // ms_Latn_MY
+ 0x6D744D544C61746ELLU, // mt_Latn_MT
+ 0xC66C494E44657661LLU, // mtr_Deva_IN
+ 0x828C434D4C61746ELLU, // mua_Latn_CM
+ 0xCA8C55534C61746ELLU, // mus_Latn_US
+ 0xE2AC504B41726162LLU, // mvy_Arab_PK
+ 0xAACC4D4C4C61746ELLU, // mwk_Latn_ML
+ 0xC6CC494E44657661LLU, // mwr_Deva_IN
+ 0xD6CC49444C61746ELLU, // mwv_Latn_ID
+ 0x8AEC5A574C61746ELLU, // mxc_Latn_ZW
+ 0x6D794D4D4D796D72LLU, // my_Mymr_MM
+ 0xD70C52554379726CLLU, // myv_Cyrl_RU
+ 0xDF0C55474C61746ELLU, // myx_Latn_UG
+ 0xE70C49524D616E64LLU, // myz_Mand_IR
+ 0xB72C495241726162LLU, // mzn_Arab_IR
+ 0x6E614E524C61746ELLU, // na_Latn_NR
+ 0xB40D434E48616E73LLU, // nan_Hans_CN
+ 0xBC0D49544C61746ELLU, // nap_Latn_IT
+ 0xC00D4E414C61746ELLU, // naq_Latn_NA
+ 0x6E624E4F4C61746ELLU, // nb_Latn_NO
+ 0x9C4D4D584C61746ELLU, // nch_Latn_MX
+ 0x6E645A574C61746ELLU, // nd_Latn_ZW
+ 0x886D4D5A4C61746ELLU, // ndc_Latn_MZ
+ 0xC86D44454C61746ELLU, // nds_Latn_DE
+ 0x6E654E5044657661LLU, // ne_Deva_NP
+ 0xD88D4E5044657661LLU, // new_Deva_NP
+ 0x6E674E414C61746ELLU, // ng_Latn_NA
+ 0xACCD4D5A4C61746ELLU, // ngl_Latn_MZ
+ 0x90ED4D584C61746ELLU, // nhe_Latn_MX
+ 0xD8ED4D584C61746ELLU, // nhw_Latn_MX
+ 0xA50D49444C61746ELLU, // nij_Latn_ID
+ 0xD10D4E554C61746ELLU, // niu_Latn_NU
+ 0xB92D494E4C61746ELLU, // njo_Latn_IN
+ 0x6E6C4E4C4C61746ELLU, // nl_Latn_NL
+ 0x998D434D4C61746ELLU, // nmg_Latn_CM
+ 0x6E6E4E4F4C61746ELLU, // nn_Latn_NO
+ 0x9DAD434D4C61746ELLU, // nnh_Latn_CM
+ 0x6E6F4E4F4C61746ELLU, // no_Latn_NO
+ 0x8DCD54484C616E61LLU, // nod_Lana_TH
+ 0x91CD494E44657661LLU, // noe_Deva_IN
+ 0xB5CD534552756E72LLU, // non_Runr_SE
+ 0xBA0D474E4E6B6F6FLLU, // nqo_Nkoo_GN
+ 0x6E725A414C61746ELLU, // nr_Latn_ZA
+ 0xAA4D434143616E73LLU, // nsk_Cans_CA
+ 0xBA4D5A414C61746ELLU, // nso_Latn_ZA
+ 0xCA8D53534C61746ELLU, // nus_Latn_SS
+ 0x6E7655534C61746ELLU, // nv_Latn_US
+ 0xC2ED434E4C61746ELLU, // nxq_Latn_CN
+ 0x6E794D574C61746ELLU, // ny_Latn_MW
+ 0xB30D545A4C61746ELLU, // nym_Latn_TZ
+ 0xB70D55474C61746ELLU, // nyn_Latn_UG
+ 0xA32D47484C61746ELLU, // nzi_Latn_GH
+ 0x6F6346524C61746ELLU, // oc_Latn_FR
+ 0x6F6D45544C61746ELLU, // om_Latn_ET
+ 0x6F72494E4F727961LLU, // or_Orya_IN
+ 0x6F7347454379726CLLU, // os_Cyrl_GE
+ 0x824E55534F736765LLU, // osa_Osge_US
+ 0xAA6E4D4E4F726B68LLU, // otk_Orkh_MN
+ 0x7061504B41726162LLU, // pa_Arab_PK
+ 0x7061494E47757275LLU, // pa_Guru_IN
+ 0x980F50484C61746ELLU, // pag_Latn_PH
+ 0xAC0F495250686C69LLU, // pal_Phli_IR
+ 0xAC0F434E50686C70LLU, // pal_Phlp_CN
+ 0xB00F50484C61746ELLU, // pam_Latn_PH
+ 0xBC0F41574C61746ELLU, // pap_Latn_AW
+ 0xD00F50574C61746ELLU, // pau_Latn_PW
+ 0x8C4F46524C61746ELLU, // pcd_Latn_FR
+ 0xB04F4E474C61746ELLU, // pcm_Latn_NG
+ 0x886F55534C61746ELLU, // pdc_Latn_US
+ 0xCC6F43414C61746ELLU, // pdt_Latn_CA
+ 0xB88F49525870656FLLU, // peo_Xpeo_IR
+ 0xACAF44454C61746ELLU, // pfl_Latn_DE
+ 0xB4EF4C4250686E78LLU, // phn_Phnx_LB
+ 0x814F494E42726168LLU, // pka_Brah_IN
+ 0xB94F4B454C61746ELLU, // pko_Latn_KE
+ 0x706C504C4C61746ELLU, // pl_Latn_PL
+ 0xC98F49544C61746ELLU, // pms_Latn_IT
+ 0xCDAF47524772656BLLU, // pnt_Grek_GR
+ 0xB5CF464D4C61746ELLU, // pon_Latn_FM
+ 0x822F504B4B686172LLU, // pra_Khar_PK
+ 0x8E2F495241726162LLU, // prd_Arab_IR
+ 0x7073414641726162LLU, // ps_Arab_AF
+ 0x707442524C61746ELLU, // pt_Latn_BR
+ 0xD28F47414C61746ELLU, // puu_Latn_GA
+ 0x717550454C61746ELLU, // qu_Latn_PE
+ 0x8A9047544C61746ELLU, // quc_Latn_GT
+ 0x9A9045434C61746ELLU, // qug_Latn_EC
+ 0xA411494E44657661LLU, // raj_Deva_IN
+ 0x945152454C61746ELLU, // rcf_Latn_RE
+ 0xA49149444C61746ELLU, // rej_Latn_ID
+ 0xB4D149544C61746ELLU, // rgn_Latn_IT
+ 0x8111494E4C61746ELLU, // ria_Latn_IN
+ 0x95114D4154666E67LLU, // rif_Tfng_MA
+ 0xC9314E5044657661LLU, // rjs_Deva_NP
+ 0xCD51424442656E67LLU, // rkt_Beng_BD
+ 0x726D43484C61746ELLU, // rm_Latn_CH
+ 0x959146494C61746ELLU, // rmf_Latn_FI
+ 0xB99143484C61746ELLU, // rmo_Latn_CH
+ 0xCD91495241726162LLU, // rmt_Arab_IR
+ 0xD19153454C61746ELLU, // rmu_Latn_SE
+ 0x726E42494C61746ELLU, // rn_Latn_BI
+ 0x99B14D5A4C61746ELLU, // rng_Latn_MZ
+ 0x726F524F4C61746ELLU, // ro_Latn_RO
+ 0x85D149444C61746ELLU, // rob_Latn_ID
+ 0x95D1545A4C61746ELLU, // rof_Latn_TZ
+ 0xB271464A4C61746ELLU, // rtm_Latn_FJ
+ 0x727552554379726CLLU, // ru_Cyrl_RU
+ 0x929155414379726CLLU, // rue_Cyrl_UA
+ 0x9A9153424C61746ELLU, // rug_Latn_SB
+ 0x727752574C61746ELLU, // rw_Latn_RW
+ 0xAAD1545A4C61746ELLU, // rwk_Latn_TZ
+ 0xD3114A504B616E61LLU, // ryu_Kana_JP
+ 0x7361494E44657661LLU, // sa_Deva_IN
+ 0x941247484C61746ELLU, // saf_Latn_GH
+ 0x9C1252554379726CLLU, // sah_Cyrl_RU
+ 0xC0124B454C61746ELLU, // saq_Latn_KE
+ 0xC81249444C61746ELLU, // sas_Latn_ID
+ 0xCC12494E4C61746ELLU, // sat_Latn_IN
+ 0xE412494E53617572LLU, // saz_Saur_IN
+ 0xBC32545A4C61746ELLU, // sbp_Latn_TZ
+ 0x736349544C61746ELLU, // sc_Latn_IT
+ 0xA852494E44657661LLU, // sck_Deva_IN
+ 0xB45249544C61746ELLU, // scn_Latn_IT
+ 0xB85247424C61746ELLU, // sco_Latn_GB
+ 0xC85243414C61746ELLU, // scs_Latn_CA
+ 0x7364504B41726162LLU, // sd_Arab_PK
+ 0x7364494E44657661LLU, // sd_Deva_IN
+ 0x7364494E4B686F6ALLU, // sd_Khoj_IN
+ 0x7364494E53696E64LLU, // sd_Sind_IN
+ 0x887249544C61746ELLU, // sdc_Latn_IT
+ 0x9C72495241726162LLU, // sdh_Arab_IR
+ 0x73654E4F4C61746ELLU, // se_Latn_NO
+ 0x949243494C61746ELLU, // sef_Latn_CI
+ 0x9C924D5A4C61746ELLU, // seh_Latn_MZ
+ 0xA0924D584C61746ELLU, // sei_Latn_MX
+ 0xC8924D4C4C61746ELLU, // ses_Latn_ML
+ 0x736743464C61746ELLU, // sg_Latn_CF
+ 0x80D249454F67616DLLU, // sga_Ogam_IE
+ 0xC8D24C544C61746ELLU, // sgs_Latn_LT
+ 0xA0F24D4154666E67LLU, // shi_Tfng_MA
+ 0xB4F24D4D4D796D72LLU, // shn_Mymr_MM
+ 0x73694C4B53696E68LLU, // si_Sinh_LK
+ 0x8D1245544C61746ELLU, // sid_Latn_ET
+ 0x736B534B4C61746ELLU, // sk_Latn_SK
+ 0xC552504B41726162LLU, // skr_Arab_PK
+ 0x736C53494C61746ELLU, // sl_Latn_SI
+ 0xA172504C4C61746ELLU, // sli_Latn_PL
+ 0xE17249444C61746ELLU, // sly_Latn_ID
+ 0x736D57534C61746ELLU, // sm_Latn_WS
+ 0x819253454C61746ELLU, // sma_Latn_SE
+ 0xA59253454C61746ELLU, // smj_Latn_SE
+ 0xB59246494C61746ELLU, // smn_Latn_FI
+ 0xBD92494C53616D72LLU, // smp_Samr_IL
+ 0xC99246494C61746ELLU, // sms_Latn_FI
+ 0x736E5A574C61746ELLU, // sn_Latn_ZW
+ 0xA9B24D4C4C61746ELLU, // snk_Latn_ML
+ 0x736F534F4C61746ELLU, // so_Latn_SO
+ 0xD1D2544854686169LLU, // sou_Thai_TH
+ 0x7371414C4C61746ELLU, // sq_Latn_AL
+ 0x737252534379726CLLU, // sr_Cyrl_RS
+ 0x737252534C61746ELLU, // sr_Latn_RS
+ 0x8632494E536F7261LLU, // srb_Sora_IN
+ 0xB63253524C61746ELLU, // srn_Latn_SR
+ 0xC632534E4C61746ELLU, // srr_Latn_SN
+ 0xDE32494E44657661LLU, // srx_Deva_IN
+ 0x73735A414C61746ELLU, // ss_Latn_ZA
+ 0xE25245524C61746ELLU, // ssy_Latn_ER
+ 0x73745A414C61746ELLU, // st_Latn_ZA
+ 0xC27244454C61746ELLU, // stq_Latn_DE
+ 0x737549444C61746ELLU, // su_Latn_ID
+ 0xAA92545A4C61746ELLU, // suk_Latn_TZ
+ 0xCA92474E4C61746ELLU, // sus_Latn_GN
+ 0x737653454C61746ELLU, // sv_Latn_SE
+ 0x7377545A4C61746ELLU, // sw_Latn_TZ
+ 0x86D2595441726162LLU, // swb_Arab_YT
+ 0x8AD243444C61746ELLU, // swc_Latn_CD
+ 0x9AD244454C61746ELLU, // swg_Latn_DE
+ 0xD6D2494E44657661LLU, // swv_Deva_IN
+ 0xB6F249444C61746ELLU, // sxn_Latn_ID
+ 0xAF12424442656E67LLU, // syl_Beng_BD
+ 0xC712495153797263LLU, // syr_Syrc_IQ
+ 0xAF32504C4C61746ELLU, // szl_Latn_PL
+ 0x7461494E54616D6CLLU, // ta_Taml_IN
+ 0xA4134E5044657661LLU, // taj_Deva_NP
+ 0xD83350484C61746ELLU, // tbw_Latn_PH
+ 0xE053494E4B6E6461LLU, // tcy_Knda_IN
+ 0x8C73434E54616C65LLU, // tdd_Tale_CN
+ 0x98734E5044657661LLU, // tdg_Deva_NP
+ 0x9C734E5044657661LLU, // tdh_Deva_NP
+ 0x7465494E54656C75LLU, // te_Telu_IN
+ 0xB093534C4C61746ELLU, // tem_Latn_SL
+ 0xB89355474C61746ELLU, // teo_Latn_UG
+ 0xCC93544C4C61746ELLU, // tet_Latn_TL
+ 0x7467504B41726162LLU, // tg_Arab_PK
+ 0x7467544A4379726CLLU, // tg_Cyrl_TJ
+ 0x7468544854686169LLU, // th_Thai_TH
+ 0xACF34E5044657661LLU, // thl_Deva_NP
+ 0xC0F34E5044657661LLU, // thq_Deva_NP
+ 0xC4F34E5044657661LLU, // thr_Deva_NP
+ 0x7469455445746869LLU, // ti_Ethi_ET
+ 0x9913455245746869LLU, // tig_Ethi_ER
+ 0xD5134E474C61746ELLU, // tiv_Latn_NG
+ 0x746B544D4C61746ELLU, // tk_Latn_TM
+ 0xAD53544B4C61746ELLU, // tkl_Latn_TK
+ 0xC553415A4C61746ELLU, // tkr_Latn_AZ
+ 0xCD534E5044657661LLU, // tkt_Deva_NP
+ 0x746C50484C61746ELLU, // tl_Latn_PH
+ 0xE173415A4C61746ELLU, // tly_Latn_AZ
+ 0x9D934E454C61746ELLU, // tmh_Latn_NE
+ 0x746E5A414C61746ELLU, // tn_Latn_ZA
+ 0x746F544F4C61746ELLU, // to_Latn_TO
+ 0x99D34D574C61746ELLU, // tog_Latn_MW
+ 0xA1F350474C61746ELLU, // tpi_Latn_PG
+ 0x747254524C61746ELLU, // tr_Latn_TR
+ 0xD23354524C61746ELLU, // tru_Latn_TR
+ 0xD63354574C61746ELLU, // trv_Latn_TW
+ 0x74735A414C61746ELLU, // ts_Latn_ZA
+ 0x8E5347524772656BLLU, // tsd_Grek_GR
+ 0x96534E5044657661LLU, // tsf_Deva_NP
+ 0x9A5350484C61746ELLU, // tsg_Latn_PH
+ 0xA653425454696274LLU, // tsj_Tibt_BT
+ 0x747452554379726CLLU, // tt_Cyrl_RU
+ 0xA67355474C61746ELLU, // ttj_Latn_UG
+ 0xCA73544854686169LLU, // tts_Thai_TH
+ 0xCE73415A4C61746ELLU, // ttt_Latn_AZ
+ 0xB2934D574C61746ELLU, // tum_Latn_MW
+ 0xAEB354564C61746ELLU, // tvl_Latn_TV
+ 0xC2D34E454C61746ELLU, // twq_Latn_NE
+ 0x9AF3434E54616E67LLU, // txg_Tang_CN
+ 0x747950464C61746ELLU, // ty_Latn_PF
+ 0xD71352554379726CLLU, // tyv_Cyrl_RU
+ 0xB3334D414C61746ELLU, // tzm_Latn_MA
+ 0xB07452554379726CLLU, // udm_Cyrl_RU
+ 0x7567434E41726162LLU, // ug_Arab_CN
+ 0x75674B5A4379726CLLU, // ug_Cyrl_KZ
+ 0x80D4535955676172LLU, // uga_Ugar_SY
+ 0x756B55414379726CLLU, // uk_Cyrl_UA
+ 0xA174464D4C61746ELLU, // uli_Latn_FM
+ 0x8594414F4C61746ELLU, // umb_Latn_AO
+ 0xC5B4494E42656E67LLU, // unr_Beng_IN
+ 0xC5B44E5044657661LLU, // unr_Deva_NP
+ 0xDDB4494E42656E67LLU, // unx_Beng_IN
+ 0x7572504B41726162LLU, // ur_Arab_PK
+ 0x757A414641726162LLU, // uz_Arab_AF
+ 0x757A555A4C61746ELLU, // uz_Latn_UZ
+ 0xA0154C5256616969LLU, // vai_Vaii_LR
+ 0x76655A414C61746ELLU, // ve_Latn_ZA
+ 0x889549544C61746ELLU, // vec_Latn_IT
+ 0xBC9552554C61746ELLU, // vep_Latn_RU
+ 0x7669564E4C61746ELLU, // vi_Latn_VN
+ 0x891553584C61746ELLU, // vic_Latn_SX
+ 0xC97542454C61746ELLU, // vls_Latn_BE
+ 0x959544454C61746ELLU, // vmf_Latn_DE
+ 0xD9954D5A4C61746ELLU, // vmw_Latn_MZ
+ 0xCDD552554C61746ELLU, // vot_Latn_RU
+ 0xBA3545454C61746ELLU, // vro_Latn_EE
+ 0xB695545A4C61746ELLU, // vun_Latn_TZ
+ 0x776142454C61746ELLU, // wa_Latn_BE
+ 0x901643484C61746ELLU, // wae_Latn_CH
+ 0xAC16455445746869LLU, // wal_Ethi_ET
+ 0xC41650484C61746ELLU, // war_Latn_PH
+ 0xBC3641554C61746ELLU, // wbp_Latn_AU
+ 0xC036494E54656C75LLU, // wbq_Telu_IN
+ 0xC436494E44657661LLU, // wbr_Deva_IN
+ 0xC97657464C61746ELLU, // wls_Latn_WF
+ 0xA1B64B4D41726162LLU, // wni_Arab_KM
+ 0x776F534E4C61746ELLU, // wo_Latn_SN
+ 0xB276494E44657661LLU, // wtm_Deva_IN
+ 0xD296434E48616E73LLU, // wuu_Hans_CN
+ 0xD41742524C61746ELLU, // xav_Latn_BR
+ 0xC457545243617269LLU, // xcr_Cari_TR
+ 0x78685A414C61746ELLU, // xh_Latn_ZA
+ 0x897754524C796369LLU, // xlc_Lyci_TR
+ 0x8D7754524C796469LLU, // xld_Lydi_TR
+ 0x9597474547656F72LLU, // xmf_Geor_GE
+ 0xB597434E4D616E69LLU, // xmn_Mani_CN
+ 0xC59753444D657263LLU, // xmr_Merc_SD
+ 0x81B753414E617262LLU, // xna_Narb_SA
+ 0xC5B7494E44657661LLU, // xnr_Deva_IN
+ 0x99D755474C61746ELLU, // xog_Latn_UG
+ 0xC5F7495250727469LLU, // xpr_Prti_IR
+ 0x8257594553617262LLU, // xsa_Sarb_YE
+ 0xC6574E5044657661LLU, // xsr_Deva_NP
+ 0xB8184D5A4C61746ELLU, // yao_Latn_MZ
+ 0xBC18464D4C61746ELLU, // yap_Latn_FM
+ 0xD418434D4C61746ELLU, // yav_Latn_CM
+ 0x8438434D4C61746ELLU, // ybb_Latn_CM
+ 0x796F4E474C61746ELLU, // yo_Latn_NG
+ 0xAE3842524C61746ELLU, // yrl_Latn_BR
+ 0x82984D584C61746ELLU, // yua_Latn_MX
+ 0x9298434E48616E73LLU, // yue_Hans_CN
+ 0x9298484B48616E74LLU, // yue_Hant_HK
+ 0x7A61434E4C61746ELLU, // za_Latn_CN
+ 0x981953444C61746ELLU, // zag_Latn_SD
+ 0xA4794B4D41726162LLU, // zdj_Arab_KM
+ 0x80994E4C4C61746ELLU, // zea_Latn_NL
+ 0x9CD94D4154666E67LLU, // zgh_Tfng_MA
+ 0x7A685457426F706FLLU, // zh_Bopo_TW
+ 0x7A68545748616E62LLU, // zh_Hanb_TW
+ 0x7A68434E48616E73LLU, // zh_Hans_CN
+ 0x7A68545748616E74LLU, // zh_Hant_TW
+ 0xB17954474C61746ELLU, // zlm_Latn_TG
+ 0xA1994D594C61746ELLU, // zmi_Latn_MY
+ 0x7A755A414C61746ELLU, // zu_Latn_ZA
+ 0x833954524C61746ELLU, // zza_Latn_TR
});
const std::unordered_map<uint32_t, uint32_t> ARAB_PARENTS({
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index 23ec5ab..f1903a5 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -1,2 +1,3 @@
set noparent
toddke@google.com
+rtmitchell@google.com
\ No newline at end of file
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 2fe98b0..63b2527 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7076,7 +7076,7 @@
}
}
- const auto& getTypeMapping() const {
+ const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const {
return mTypeMapping->mData;
}
@@ -7137,9 +7137,6 @@
const PackageGroup* packageGroup = mPackageGroups[0];
- // the number of resources overlaid that were not explicitly marked overlayable
- size_t forcedOverlayCount = 0u;
-
// find the resources that exist in both packages
auto typeMapping = std::make_unique<IdmapTypeMapping>();
for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
@@ -7170,11 +7167,6 @@
continue;
}
- if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
- ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
- ++forcedOverlayCount;
- }
-
typeMapping->add(target_resid, overlay_resid);
}
}
@@ -7243,10 +7235,6 @@
typeData += entryCount * 2;
}
- if (forcedOverlayCount > 0) {
- ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount);
- }
-
return NO_ERROR;
}
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index 99a52dc..a0f2343 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -89,7 +89,9 @@
len_(len),
last_error_(nullptr) {
CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
- VerifyNextChunk();
+ if (len_ != 0) {
+ VerifyNextChunk();
+ }
}
Chunk Next();
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 349b379..8c5c3b7 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -20,6 +20,7 @@
#include <memory>
#include <set>
#include <vector>
+#include <unordered_set>
#include "android-base/macros.h"
@@ -76,6 +77,10 @@
// TypeSpecPtr is a managed pointer that knows how to delete itself.
using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+struct OverlayableInfo {
+ uint32_t policy_flags;
+};
+
class LoadedPackage {
public:
class iterator {
@@ -216,6 +221,18 @@
}
}
+ // Retrieve the overlayable properties of the specified resource. If the resource is not
+ // overlayable, this will return a null pointer.
+ const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const {
+ for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids
+ : overlayable_infos_) {
+ if (overlayable_info_ids.second.find(resid) != overlayable_info_ids.second.end()) {
+ return &overlayable_info_ids.first;
+ }
+ }
+ return nullptr;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
@@ -233,6 +250,7 @@
ByteBucketArray<TypeSpecPtr> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
+ std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
};
// Read-only view into a resource table. This class validates all data
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index ad33fcf..cf2d8fb 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -234,7 +234,9 @@
RES_TABLE_PACKAGE_TYPE = 0x0200,
RES_TABLE_TYPE_TYPE = 0x0201,
RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
- RES_TABLE_LIBRARY_TYPE = 0x0203
+ RES_TABLE_LIBRARY_TYPE = 0x0203,
+ RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
+ RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205,
};
/**
@@ -1354,10 +1356,6 @@
enum : uint32_t {
// Additional flag indicating an entry is public.
SPEC_PUBLIC = 0x40000000u,
-
- // Additional flag indicating an entry is overlayable at runtime.
- // Added in Android-P.
- SPEC_OVERLAYABLE = 0x80000000u,
};
};
@@ -1607,6 +1605,49 @@
uint16_t packageName[128];
};
+/**
+ * Specifies the set of resources that are explicitly allowed to be overlaid by RROs.
+ */
+struct ResTable_overlayable_header
+{
+ struct ResChunk_header header;
+};
+
+/**
+ * Holds a list of resource ids that are protected from being overlaid by a set of policies. If
+ * the overlay fulfils at least one of the policies, then the overlay can overlay the list of
+ * resources.
+ */
+struct ResTable_overlayable_policy_header
+{
+ struct ResChunk_header header;
+
+ enum PolicyFlags : uint32_t {
+ // Any overlay can overlay these resources.
+ POLICY_PUBLIC = 0x00000001,
+
+ // The overlay must reside of the system partition or must have existed on the system partition
+ // before an upgrade to overlay these resources.
+ POLICY_SYSTEM_PARTITION = 0x00000002,
+
+ // The overlay must reside of the vendor partition or must have existed on the vendor partition
+ // before an upgrade to overlay these resources.
+ POLICY_VENDOR_PARTITION = 0x00000004,
+
+ // The overlay must reside of the product partition or must have existed on the product
+ // partition before an upgrade to overlay these resources.
+ POLICY_PRODUCT_PARTITION = 0x00000008,
+
+ // The overlay must reside of the product services partition or must have existed on the product
+ // services partition before an upgrade to overlay these resources.
+ POLICY_PRODUCT_SERVICES_PARTITION = 0x00000010,
+ };
+ uint32_t policy_flags;
+
+ // The number of ResTable_ref that follow this header.
+ uint32_t entry_count;
+};
+
struct alignas(uint32_t) Idmap_header {
// Always 0x504D4449 ('IDMP')
uint32_t magic;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index ffa4836..441356b 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -22,12 +22,14 @@
#include "TestHelpers.h"
#include "data/basic/R.h"
#include "data/libclient/R.h"
+#include "data/overlayable/R.h"
#include "data/sparse/R.h"
#include "data/styles/R.h"
namespace app = com::android::app;
namespace basic = com::android::basic;
namespace libclient = com::android::libclient;
+namespace overlayable = com::android::overlayable;
namespace sparse = com::android::sparse;
using ::android::base::ReadFileToString;
@@ -273,10 +275,44 @@
ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
}
-// structs with size fields (like Res_value, ResTable_entry) should be
-// backwards and forwards compatible (aka checking the size field against
-// sizeof(Res_value) might not be backwards compatible.
-TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+TEST(LoadedArscTest, LoadOverlayable) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
+ "resources.arsc", &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+ false /*load_as_shared_library*/);
+
+ ASSERT_THAT(loaded_arsc, NotNull());
+ const LoadedPackage* package = loaded_arsc->GetPackageById(
+ get_package_id(overlayable::R::string::not_overlayable));
+
+ const OverlayableInfo* info = package->GetOverlayableInfo(
+ overlayable::R::string::not_overlayable);
+ ASSERT_THAT(info, IsNull());
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable1);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags,
+ Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
+ | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags,
+ Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
+ | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION
+ | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+}
TEST(LoadedArscTest, ResourceIdentifierIterator) {
std::string contents;
@@ -326,4 +362,9 @@
ASSERT_EQ(end, iter);
}
+// structs with size fields (like Res_value, ResTable_entry) should be
+// backwards and forwards compatible (aka checking the size field against
+// sizeof(Res_value) might not be backwards compatible.
+TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+
} // namespace android
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index 33f9611..d37874d 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/packages/SystemUI/res/anim/system_in.xml b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
similarity index 69%
rename from packages/SystemUI/res/anim/system_in.xml
rename to libs/androidfw/tests/data/overlayable/AndroidManifest.xml
index 630fd72..abc2a45 100644
--- a/packages/SystemUI/res/anim/system_in.xml
+++ b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +14,8 @@
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- >
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_longAnimTime"
- />
-</set>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlayable">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h
new file mode 100644
index 0000000..e46e264d
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/R.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTS_DATA_OVERLAYABLE_R_H_
+#define TESTS_DATA_OVERLAYABLE_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace overlayable {
+
+struct R {
+ struct string {
+ enum : uint32_t {
+ not_overlayable = 0x7f010000,
+ overlayable1 = 0x7f010001,
+ overlayable2 = 0x7f010002,
+ overlayable3 = 0x7f010003,
+ overlayable4 = 0x7f010004,
+ };
+ };
+};
+
+} // namespace overlayable
+} // namespace android
+} // namespace com
+
+#endif /* TESTS_DATA_OVERLAYABLE_R_H_ */
diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build
new file mode 100755
index 0000000..98fdc51
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
new file mode 100644
index 0000000..85ab4be
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
new file mode 100644
index 0000000..11aa735
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+<overlayable>
+ <!-- Any overlay can overlay the value of @string/overlayable1 -->
+ <item type="string" name="overlayable1" />
+
+ <!-- Any overlay on the product or system partition can overlay the value of
+ @string/overlayable2 -->
+ <policy type="product|system">
+ <item type="string" name="overlayable2" />
+ </policy>
+
+ <!-- Any overlay can overlay the value of @string/overlayable4 -->
+ <policy type="public">
+ <item type="string" name="overlayable4" />
+ </policy>
+</overlayable>
+
+<overlayable>
+ <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of
+ @string/overlayable3 -->
+ <policy type="product_services|vendor|product">
+ <item type="string" name="overlayable3" />
+ </policy>
+</overlayable>
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml
new file mode 100644
index 0000000..5676d7c
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <public type="string" name="not_overlayable" id="0x7f010000" />
+ <public type="string" name="overlayable1" id="0x7f010001" />
+ <public type="string" name="overlayable2" id="0x7f010002" />
+ <public type="string" name="overlayable3" id="0x7f010003" />
+ <public type="string" name="overlayable4" id="0x7f010004" />
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/system_in.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml
similarity index 61%
copy from packages/SystemUI/res/anim/system_in.xml
copy to libs/androidfw/tests/data/overlayable/res/values/values.xml
index 630fd72..a86b312 100644
--- a/packages/SystemUI/res/anim/system_in.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +14,10 @@
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- >
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_longAnimTime"
- />
-</set>
+<resources>
+ <string name="not_overlayable">Not overlayable</string>
+ <string name="overlayable1">Overlayable One</string>
+ <string name="overlayable2">Overlayable Two</string>
+ <string name="overlayable3">Overlayable Three</string>
+ <string name="overlayable4">Overlayable Four</string>
+</resources>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index da77b99..d22eaf3 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -171,12 +171,14 @@
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/SkiaVulkanPipeline.cpp",
"pipeline/skia/VectorDrawableAtlas.cpp",
+ "pipeline/skia/VkFunctorDrawable.cpp",
"pipeline/skia/VkInteropFunctorDrawable.cpp",
"renderstate/RenderState.cpp",
"renderthread/CacheManager.cpp",
"renderthread/CanvasContext.cpp",
"renderthread/DrawFrameTask.cpp",
"renderthread/EglManager.cpp",
+ "renderthread/ReliableSurface.cpp",
"renderthread/VulkanManager.cpp",
"renderthread/RenderProxy.cpp",
"renderthread/RenderTask.cpp",
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 4a3e10c..8067313 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -29,8 +29,6 @@
namespace android {
namespace uirenderer {
-bool Properties::drawDeferDisabled = false;
-bool Properties::drawReorderDisabled = false;
bool Properties::debugLayersUpdates = false;
bool Properties::debugOverdraw = false;
bool Properties::showDirtyRegions = false;
@@ -40,7 +38,6 @@
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
-StencilClipDebug Properties::debugStencilClip = StencilClipDebug::Hide;
float Properties::overrideLightRadius = -1.0f;
float Properties::overrideLightPosY = -1.0f;
@@ -85,7 +82,6 @@
char property[PROPERTY_VALUE_MAX];
bool prevDebugLayersUpdates = debugLayersUpdates;
bool prevDebugOverdraw = debugOverdraw;
- StencilClipDebug prevDebugStencilClip = debugStencilClip;
debugOverdraw = false;
if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) {
@@ -99,20 +95,6 @@
}
}
- // See Properties.h for valid values
- if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) {
- INIT_LOGD(" Stencil clip debug enabled: %s", property);
- if (!strcmp(property, "hide")) {
- debugStencilClip = StencilClipDebug::Hide;
- } else if (!strcmp(property, "highlight")) {
- debugStencilClip = StencilClipDebug::ShowHighlight;
- } else if (!strcmp(property, "region")) {
- debugStencilClip = StencilClipDebug::ShowRegion;
- }
- } else {
- debugStencilClip = StencilClipDebug::Hide;
- }
-
sProfileType = ProfileType::None;
if (property_get(PROPERTY_PROFILE, property, "") > 0) {
if (!strcmp(property, PROPERTY_PROFILE_VISUALIZE_BARS)) {
@@ -125,12 +107,6 @@
debugLayersUpdates = property_get_bool(PROPERTY_DEBUG_LAYERS_UPDATES, false);
INIT_LOGD(" Layers updates debug enabled: %d", debugLayersUpdates);
- drawDeferDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_DEFER, false);
- INIT_LOGD(" Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
-
- drawReorderDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_REORDER, false);
- INIT_LOGD(" Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
-
showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
debugLevel = (DebugLevel)property_get_int(PROPERTY_DEBUG, kDebugDisabled);
@@ -152,8 +128,7 @@
enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true);
- return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) ||
- (prevDebugStencilClip != debugStencilClip);
+ return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
void Properties::overrideProperty(const char* name, const char* value) {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index da53f66..0a7f4e7 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -95,20 +95,6 @@
#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars"
/**
- * Used to enable/disable non-rectangular clipping debugging.
- *
- * The accepted values are:
- * "highlight", drawing commands clipped by the stencil will
- * be colored differently
- * "region", renders the clipping region on screen whenever
- * the stencil is set
- * "hide", don't show the clip
- *
- * The default value is "hide".
- */
-#define PROPERTY_DEBUG_STENCIL_CLIP "debug.hwui.show_non_rect_clip"
-
-/**
* Turn on to draw dirty regions every other frame.
*
* Possible values:
@@ -118,19 +104,6 @@
#define PROPERTY_DEBUG_SHOW_DIRTY_REGIONS "debug.hwui.show_dirty_regions"
/**
- * Disables draw operation deferral if set to "true", forcing draw
- * commands to be issued to OpenGL in order, and processed in sequence
- * with state-manipulation canvas commands.
- */
-#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
-
-/**
- * Used to disable draw operation reordering when deferring draw operations
- * Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
- */
-#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
-
-/**
* Setting this property will enable or disable the dropping of frames with
* empty damage. Default is "true".
*/
@@ -207,8 +180,6 @@
enum class OverdrawColorSet { Default = 0, Deuteranomaly };
-enum class StencilClipDebug { Hide, ShowHighlight, ShowRegion };
-
enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };
/**
@@ -220,8 +191,6 @@
public:
static bool load();
- static bool drawDeferDisabled;
- static bool drawReorderDisabled;
static bool debugLayersUpdates;
static bool debugOverdraw;
static bool showDirtyRegions;
@@ -235,7 +204,6 @@
static DebugLevel debugLevel;
static OverdrawColorSet overdrawColorSet;
- static StencilClipDebug debugStencilClip;
// Override the value for a subset of properties in this class
static void overrideProperty(const char* name, const char* value);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index c63e449..0b847af 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -912,18 +912,6 @@
fDL->drawAnnotation(rect, key, val);
}
-void RecordingCanvas::onDrawText(const void* text, size_t bytes, SkScalar x, SkScalar y,
- const SkPaint& paint) {
- fDL->drawText(text, bytes, x, y, paint);
-}
-void RecordingCanvas::onDrawPosText(const void* text, size_t bytes, const SkPoint pos[],
- const SkPaint& paint) {
- fDL->drawPosText(text, bytes, pos, paint);
-}
-void RecordingCanvas::onDrawPosTextH(const void* text, size_t bytes, const SkScalar xs[],
- SkScalar y, const SkPaint& paint) {
- fDL->drawPosTextH(text, bytes, xs, y, paint);
-}
void RecordingCanvas::onDrawTextRSXform(const void* text, size_t bytes, const SkRSXform xform[],
const SkRect* cull, const SkPaint& paint) {
fDL->drawTextRSXform(text, bytes, xform, cull, paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 08cfc62..35cf707 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -170,10 +170,6 @@
void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
- void onDrawText(const void*, size_t, SkScalar x, SkScalar y, const SkPaint&) override;
- void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override;
- void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override;
-
void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
const SkPaint&) override;
void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 4a63910..00ce28a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -454,6 +454,9 @@
using StringBuffer = FatVector<char, 128>;
template <typename... T>
+// TODO:__printflike(2, 3)
+// Doesn't work because the warning doesn't understand string_view and doesn't like that
+// it's not a C-style variadic function.
static void format(StringBuffer& buffer, const std::string_view& format, T... args) {
buffer.resize(buffer.capacity());
while (1) {
@@ -468,19 +471,20 @@
buffer.resize(needed + 1);
return;
}
- buffer.resize(buffer.size() * 2);
+ // If we're doing a heap alloc anyway might as well give it some slop
+ buffer.resize(needed + 100);
}
}
void RenderNode::markDrawStart(SkCanvas& canvas) {
StringBuffer buffer;
- format(buffer, "RenderNode(id=%d, name='%s')", uniqueId(), getName());
+ format(buffer, "RenderNode(id=%" PRId64 ", name='%s')", uniqueId(), getName());
canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
}
void RenderNode::markDrawEnd(SkCanvas& canvas) {
StringBuffer buffer;
- format(buffer, "/RenderNode(id=%d, name='%s')", uniqueId(), getName());
+ format(buffer, "/RenderNode(id=%" PRId64 ", name='%s')", uniqueId(), getName());
canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index ba34384..6be7ef7 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -681,7 +681,7 @@
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
}
- SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+ SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
// Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
// older.
if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0 &&
@@ -708,7 +708,7 @@
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
}
- SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+ SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
const int N = end - start;
SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform)));
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 769fce4..84292c8 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -43,7 +43,7 @@
static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint,
const minikin::MinikinPaint& paint,
const minikin::FontFakery& fakery) {
- skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ skPaint->setTextEncoding(kGlyphID_SkTextEncoding);
skPaint->setTextSize(paint.size);
skPaint->setTextScaleX(paint.scaleX);
skPaint->setTextSkewX(paint.skewX);
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 2062194..2b5d580 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -82,18 +82,6 @@
mOutput << mIdent << "drawDRRect" << std::endl;
}
- void onDrawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {
- mOutput << mIdent << "drawText" << std::endl;
- }
-
- void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override {
- mOutput << mIdent << "drawPosText" << std::endl;
- }
-
- void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override {
- mOutput << mIdent << "drawPosTextH" << std::endl;
- }
-
void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
const SkPaint&) override {
mOutput << mIdent << "drawTextRSXform" << std::endl;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 142bca9..07979a2 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -155,7 +155,7 @@
}
}
-bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
ColorMode colorMode) {
if (mEglSurface != EGL_NO_SURFACE) {
mEglManager.destroySurface(mEglSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 4ab3541..47991069 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -42,7 +42,7 @@
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+ bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b682ab0..b56c3ef 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -25,6 +25,7 @@
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/VkInteropFunctorDrawable.h"
+#include "pipeline/skia/VkFunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -124,6 +125,8 @@
uirenderer::GlFunctorLifecycleListener* listener) {
FunctorDrawable* functorDrawable;
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
+ // interop is disabled/moved.
functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
listener, asSkCanvas());
} else {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index a494e49..437b5dc 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -115,15 +115,16 @@
void SkiaVulkanPipeline::onStop() {}
-bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
ColorMode colorMode) {
if (mVkSurface) {
mVkManager.destroySurface(mVkSurface);
mVkSurface = nullptr;
}
+ mSurfaceColorSpace = SkColorSpace::MakeSRGB();
if (surface) {
- mVkSurface = mVkManager.createSurface(surface, colorMode);
+ mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace);
}
if (colorMode == ColorMode::SRGB) {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 14c0d69..02874c7 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -38,7 +38,7 @@
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+ bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
new file mode 100644
index 0000000..71ad5e1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VkFunctorDrawable.h"
+#include <private/hwui/DrawVkInfo.h>
+
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <GrBackendDrawableInfo.h>
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+#include <SkImage.h>
+#include <vk/GrVkTypes.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor)
+ : INHERITED()
+ , mFunctor(functor) {}
+
+VkFunctorDrawHandler::~VkFunctorDrawHandler() {
+ // TODO(cblume) Fill in the DrawVkInfo parameters.
+ (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr);
+}
+
+void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
+ ATRACE_CALL();
+
+ GrVkDrawableInfo vulkan_info;
+ if (!info.getVkDrawableInfo(&vulkan_info)) {
+ return;
+ }
+
+ DrawVkInfo draw_vk_info;
+ // TODO(cblume) Fill in the rest of the parameters and test the actual call.
+ draw_vk_info.isLayer = true;
+
+ (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
+}
+
+VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
+ SkCanvas* canvas)
+ : FunctorDrawable(functor, listener, canvas) {}
+
+VkFunctorDrawable::~VkFunctorDrawable() = default;
+
+void VkFunctorDrawable::syncFunctor() const {
+ (*mFunctor)(DrawVkInfo::kModeSync, nullptr);
+}
+
+void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
+ LOG_ALWAYS_FATAL("VkFunctorDrawable::onDraw() should never be called.");
+ // Instead of calling onDraw(), the call should come from onSnapGpuDrawHandler.
+}
+
+std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
+ GrBackendApi backendApi, const SkMatrix& matrix) {
+ if (backendApi != GrBackendApi::kVulkan) {
+ return nullptr;
+ }
+ std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor));
+ return std::move(draw);
+}
+
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
new file mode 100644
index 0000000..5cd1314
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+#include <SkImageInfo.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * This draw handler will be returned by VkFunctorDrawable's onSnapGpuDrawHandler. It allows us to
+ * issue Vulkan commands while the command buffer is being flushed.
+ */
+class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler {
+public:
+ explicit VkFunctorDrawHandler(Functor* functor);
+ ~VkFunctorDrawHandler() override;
+
+ void draw(const GrBackendDrawableInfo& info) override;
+private:
+ typedef GpuDrawHandler INHERITED;
+
+ Functor* mFunctor;
+};
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list of Skia drawing
+ * commands.
+ */
+class VkFunctorDrawable : public FunctorDrawable {
+public:
+ VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
+ SkCanvas* canvas);
+ ~VkFunctorDrawable() override;
+
+ void syncFunctor() const override;
+
+protected:
+ // SkDrawable functions:
+ void onDraw(SkCanvas* canvas) override;
+ std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
+ const SkMatrix& matrix) override;
+};
+
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 004a558..8228550 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -46,7 +46,7 @@
ScopedDrawRequest() { beginDraw(); }
private:
void beginDraw() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
if (!sGLDrawThread) {
sGLDrawThread = new ThreadBase{};
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
new file mode 100644
index 0000000..019950f
--- /dev/null
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DRAW_VK_INFO_H
+#define ANDROID_HWUI_DRAW_VK_INFO_H
+
+#include <vulkan/vulkan.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan
+ * functors.
+ */
+struct DrawVkInfo {
+ // Input: current width/height of destination surface
+ int width;
+ int height;
+
+ // Input: is the render target an FBO
+ bool isLayer;
+
+ // Input: current transform matrix, in OpenGL format
+ float transform[16];
+
+ // Input: WebView should do its main compositing draws into this. It cannot do anything that
+ // would require stopping the render pass.
+ VkCommandBuffer secondaryCommandBuffer;
+
+ // Input: The main color attachment index where secondaryCommandBuffer will eventually be
+ // submitted.
+ uint32_t colorAttachmentIndex;
+
+ // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer
+ // will be submitted into.
+ VkRenderPass compatibleRenderPass;
+
+ // Input: Format of the destination surface.
+ VkFormat format;
+
+ // Input: Color space transfer params
+ float G;
+ float A;
+ float B;
+ float C;
+ float D;
+ float E;
+ float F;
+
+ // Input: Color space transformation from linear RGB to D50-adapted XYZ
+ float matrix[9];
+
+ // Input: current clip rect
+ int clipLeft;
+ int clipTop;
+ int clipRight;
+ int clipBottom;
+
+ /**
+ * Values used as the "what" parameter of the functor.
+ */
+ enum Mode {
+ // Called once at WebView start
+ kModeInit,
+ // Called when things need to be re-created
+ kModeReInit,
+ // Notifies the app that the composite functor will be called soon. This allows WebView to
+ // begin work early.
+ kModePreComposite,
+ // Do the actual composite work
+ kModeComposite,
+ // This allows WebView to begin using the previously submitted objects in future work.
+ kModePostComposite,
+ // Invoked every time the UI thread pushes over a frame to the render thread and the owning
+ // view has a dirty display list*. This is a signal to sync any data that needs to be
+ // shared between the UI thread and the render thread. During this time the UI thread is
+ // blocked.
+ kModeSync
+ };
+
+ /**
+ * Values used by Vulkan functors to tell the framework what to do next.
+ */
+ enum Status {
+ // The functor is done
+ kStatusDone = 0x0,
+ };
+}; // struct DrawVkInfo
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // ANDROID_HWUI_DRAW_VK_INFO_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f1a522e..4e4262c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -142,7 +142,12 @@
void CanvasContext::setSurface(sp<Surface>&& surface) {
ATRACE_CALL();
- mNativeSurface = std::move(surface);
+ if (surface) {
+ mNativeSurface = new ReliableSurface{std::move(surface)};
+ mNativeSurface->setDequeueTimeout(500_ms);
+ } else {
+ mNativeSurface = nullptr;
+ }
ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode);
@@ -285,10 +290,11 @@
info.damageAccumulator = &mDamageAccumulator;
info.layerUpdateQueue = &mLayerUpdateQueue;
+ info.out.canDrawThisFrame = true;
mAnimationContext->startFrame(info.mode);
mRenderPipeline->onPrepareTree();
- for (const sp<RenderNode>& node : mRenderNodes) {
+ for (const sp<RenderNode> &node : mRenderNodes) {
// Only the primary target node will be drawn full - all other nodes would get drawn in
// real time mode. In case of a window, the primary node is the window content and the other
// node(s) are non client / filler nodes.
@@ -304,7 +310,7 @@
mIsDirty = true;
- if (CC_UNLIKELY(!mNativeSurface.get())) {
+ if (CC_UNLIKELY(!hasSurface())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
info.out.canDrawThisFrame = false;
return;
@@ -312,7 +318,7 @@
if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) {
nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
- SwapHistory& lastSwap = mSwapHistory.back();
+ SwapHistory &lastSwap = mSwapHistory.back();
nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
// The slight fudge-factor is to deal with cases where
// the vsync was estimated due to being slow handling the signal.
@@ -323,25 +329,6 @@
// the deadline for RT animations
info.out.canDrawThisFrame = false;
}
- /* This logic exists to try and recover from a display latch miss, which essentially
- * results in the bufferqueue being double-buffered instead of triple-buffered.
- * SurfaceFlinger itself now tries to handle & recover from this situation, so this
- * logic should no longer be necessary. As it's occasionally triggering when
- * undesired disable it.
- * TODO: Remove this entirely if the results are solid.
- else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
- (latestVsync - mLastDropVsync) < 500_ms) {
- // It's been several frame intervals, assume the buffer queue is fine
- // or the last drop was too recent
- info.out.canDrawThisFrame = true;
- } else {
- info.out.canDrawThisFrame = !isSwapChainStuffed();
- if (!info.out.canDrawThisFrame) {
- // dropping frame
- mLastDropVsync = mRenderThread.timeLord().latestVsync();
- }
- }
- */
} else {
info.out.canDrawThisFrame = true;
}
@@ -352,7 +339,19 @@
info.out.canDrawThisFrame = false;
}
- if (!info.out.canDrawThisFrame) {
+ if (info.out.canDrawThisFrame) {
+ int err = mNativeSurface->reserveNext();
+ if (err != OK) {
+ mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+ info.out.canDrawThisFrame = false;
+ ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err));
+ if (err != TIMED_OUT) {
+ // A timed out surface can still recover, but assume others are permanently dead.
+ setSurface(nullptr);
+ return;
+ }
+ }
+ } else {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 70be4a6..9e7abf4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,6 +25,7 @@
#include "IRenderPipeline.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
+#include "ReliableSurface.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "thread/Task.h"
@@ -219,7 +220,7 @@
EGLint mLastFrameHeight = 0;
RenderThread& mRenderThread;
- sp<Surface> mNativeSurface;
+ sp<ReliableSurface> mNativeSurface;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
bool mStopped = false;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 65ced6a..8230dfd 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -31,6 +31,8 @@
#include <string>
#include <vector>
+#include <system/window.h>
+#include <gui/Surface.h>
#define GLES_VERSION 2
@@ -106,7 +108,7 @@
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
"Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
- ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+ ALOGV("Initialized EGL, version %d.%d", (int)major, (int)minor);
initExtensions();
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 4972554..42e17b273 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -28,9 +28,9 @@
class GrContext;
-namespace android {
+struct ANativeWindow;
-class Surface;
+namespace android {
namespace uirenderer {
@@ -67,7 +67,7 @@
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
- virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
+ virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
virtual void onStop() = 0;
virtual bool isSurfaceReady() = 0;
virtual bool isContextReady() = 0;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
new file mode 100644
index 0000000..6f2b9df
--- /dev/null
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReliableSurface.h"
+
+#include <private/android/AHardwareBufferHelpers.h>
+
+namespace android::uirenderer::renderthread {
+
+// TODO: Re-enable after addressing more of the TODO's
+// With this disabled we won't have a good up-front signal that the surface is no longer valid,
+// however we can at least handle that reactively post-draw. There's just not a good mechanism
+// to propagate this error back to the caller
+constexpr bool DISABLE_BUFFER_PREFETCH = true;
+
+// TODO: Make surface less protected
+// This exists because perform is a varargs, and ANativeWindow has no va_list perform.
+// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do
+// that instead
+struct SurfaceExposer : Surface {
+ // Make warnings happy
+ SurfaceExposer() = delete;
+
+ using Surface::setBufferCount;
+ using Surface::setSwapInterval;
+ using Surface::dequeueBuffer;
+ using Surface::queueBuffer;
+ using Surface::cancelBuffer;
+ using Surface::lockBuffer_DEPRECATED;
+ using Surface::perform;
+};
+
+#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__)
+
+ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
+ LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
+
+ ANativeWindow::setSwapInterval = hook_setSwapInterval;
+ ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
+ ANativeWindow::cancelBuffer = hook_cancelBuffer;
+ ANativeWindow::queueBuffer = hook_queueBuffer;
+ ANativeWindow::query = hook_query;
+ ANativeWindow::perform = hook_perform;
+
+ ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
+ ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
+ ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
+ ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
+}
+
+ReliableSurface::~ReliableSurface() {
+ clearReservedBuffer();
+}
+
+void ReliableSurface::perform(int operation, va_list args) {
+ std::lock_guard _lock{mMutex};
+
+ switch (operation) {
+ case NATIVE_WINDOW_SET_USAGE:
+ mUsage = va_arg(args, uint32_t);
+ break;
+ case NATIVE_WINDOW_SET_USAGE64:
+ mUsage = va_arg(args, uint64_t);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+ /* width */ va_arg(args, uint32_t);
+ /* height */ va_arg(args, uint32_t);
+ mFormat = va_arg(args, PixelFormat);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ mFormat = va_arg(args, PixelFormat);
+ break;
+ }
+}
+
+int ReliableSurface::reserveNext() {
+ {
+ std::lock_guard _lock{mMutex};
+ if (mReservedBuffer) {
+ ALOGW("reserveNext called but there was already a buffer reserved?");
+ return OK;
+ }
+ if (mInErrorState) {
+ return UNKNOWN_ERROR;
+ }
+ if (mHasDequeuedBuffer) {
+ return OK;
+ }
+ if constexpr (DISABLE_BUFFER_PREFETCH) {
+ return OK;
+ }
+ }
+
+ // TODO: Update this to better handle when requested dimensions have changed
+ // Currently the driver does this via query + perform but that's after we've already
+ // reserved a buffer. Should we do that logic instead? Or should we drop
+ // the backing Surface to the ground and go full manual on the IGraphicBufferProducer instead?
+
+ int fenceFd = -1;
+ ANativeWindowBuffer* buffer = nullptr;
+ int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd);
+
+ {
+ std::lock_guard _lock{mMutex};
+ LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext");
+ mReservedBuffer = buffer;
+ mReservedFenceFd.reset(fenceFd);
+ }
+
+ return result;
+}
+
+void ReliableSurface::clearReservedBuffer() {
+ ANativeWindowBuffer* buffer = nullptr;
+ int releaseFd = -1;
+ {
+ std::lock_guard _lock{mMutex};
+ if (mReservedBuffer) {
+ ALOGW("Reserved buffer %p was never used", mReservedBuffer);
+ buffer = mReservedBuffer;
+ releaseFd = mReservedFenceFd.release();
+ }
+ mReservedBuffer = nullptr;
+ mReservedFenceFd.reset();
+ mHasDequeuedBuffer = false;
+ }
+ if (buffer) {
+ callProtected(mSurface, cancelBuffer, buffer, releaseFd);
+ }
+}
+
+int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+ clearReservedBuffer();
+ if (isFallbackBuffer(buffer)) {
+ if (fenceFd > 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+ int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd);
+ return result;
+}
+
+int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
+ {
+ std::lock_guard _lock{mMutex};
+ if (mReservedBuffer) {
+ *buffer = mReservedBuffer;
+ *fenceFd = mReservedFenceFd.release();
+ mReservedBuffer = nullptr;
+ return OK;
+ }
+ }
+
+ int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
+ if (result != OK) {
+ ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
+ *buffer = acquireFallbackBuffer();
+ *fenceFd = -1;
+ return *buffer ? OK : INVALID_OPERATION;
+ } else {
+ std::lock_guard _lock{mMutex};
+ mHasDequeuedBuffer = true;
+ }
+ return OK;
+}
+
+int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+ clearReservedBuffer();
+
+ if (isFallbackBuffer(buffer)) {
+ if (fenceFd > 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+
+ int result = callProtected(mSurface, queueBuffer, buffer, fenceFd);
+ return result;
+}
+
+bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
+ if (!mScratchBuffer || !windowBuffer) {
+ return false;
+ }
+ ANativeWindowBuffer* scratchBuffer =
+ AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
+ return windowBuffer == scratchBuffer;
+}
+
+ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() {
+ std::lock_guard _lock{mMutex};
+ mInErrorState = true;
+
+ if (mScratchBuffer) {
+ return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
+ }
+
+ AHardwareBuffer_Desc desc;
+ desc.usage = mUsage;
+ desc.format = mFormat;
+ desc.width = 1;
+ desc.height = 1;
+ desc.layers = 1;
+ desc.rfu0 = 0;
+ desc.rfu1 = 0;
+ AHardwareBuffer* newBuffer = nullptr;
+ int err = AHardwareBuffer_allocate(&desc, &newBuffer);
+ if (err) {
+ // Allocate failed, that sucks
+ ALOGW("Failed to allocate scratch buffer, error=%d", err);
+ return nullptr;
+ }
+ mScratchBuffer.reset(newBuffer);
+ return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
+}
+
+Surface* ReliableSurface::getWrapped(const ANativeWindow* window) {
+ return getSelf(window)->mSurface.get();
+}
+
+int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) {
+ return callProtected(getWrapped(window), setSwapInterval, interval);
+}
+
+int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd) {
+ return getSelf(window)->dequeueBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd) {
+ return getSelf(window)->cancelBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd) {
+ return getSelf(window)->queueBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer** buffer) {
+ ANativeWindowBuffer* buf;
+ int fenceFd = -1;
+ int result = window->dequeueBuffer(window, &buf, &fenceFd);
+ if (result != OK) {
+ return result;
+ }
+ sp<Fence> fence(new Fence(fenceFd));
+ int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
+ if (waitResult != OK) {
+ ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult);
+ window->cancelBuffer(window, buf, -1);
+ return waitResult;
+ }
+ *buffer = buf;
+ return result;
+}
+
+int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return window->cancelBuffer(window, buffer, -1);
+}
+
+int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ // This method is a no-op in Surface as well
+ return OK;
+}
+
+int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return window->queueBuffer(window, buffer, -1);
+}
+
+int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) {
+ return getWrapped(window)->query(what, value);
+}
+
+int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
+ // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions
+ // TODO: Filter to things that only affect the reserved buffer
+ // TODO: Can we mutate the reserved buffer in some cases?
+ getSelf(window)->clearReservedBuffer();
+ va_list args;
+ va_start(args, operation);
+ int result = callProtected(getWrapped(window), perform, operation, args);
+ va_end(args);
+
+ switch (operation) {
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ case NATIVE_WINDOW_SET_USAGE:
+ case NATIVE_WINDOW_SET_USAGE64:
+ va_start(args, operation);
+ getSelf(window)->perform(operation, args);
+ va_end(args);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+}; // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
new file mode 100644
index 0000000..0bfc72e
--- /dev/null
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/Surface.h>
+#include <utils/Macros.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+
+namespace android::uirenderer::renderthread {
+
+class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> {
+ PREVENT_COPY_AND_ASSIGN(ReliableSurface);
+
+public:
+ ReliableSurface(sp<Surface>&& surface);
+ ~ReliableSurface();
+
+ void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); }
+
+ int reserveNext();
+
+ void allocateBuffers() { mSurface->allocateBuffers(); }
+
+ int query(int what, int* value) const { return mSurface->query(what, value); }
+
+ nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); }
+
+ uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); }
+
+private:
+ const sp<Surface> mSurface;
+
+ mutable std::mutex mMutex;
+
+ uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+ PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888;
+ std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{
+ nullptr, AHardwareBuffer_release};
+ ANativeWindowBuffer* mReservedBuffer = nullptr;
+ base::unique_fd mReservedFenceFd;
+ bool mHasDequeuedBuffer = false;
+ bool mInErrorState = false;
+
+ bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const;
+ ANativeWindowBuffer* acquireFallbackBuffer();
+ void clearReservedBuffer();
+
+ void perform(int operation, va_list args);
+ int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+ int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
+ int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+
+ static Surface* getWrapped(const ANativeWindow*);
+
+ // ANativeWindow hooks
+ static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+ static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+ static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
+ static int hook_perform(ANativeWindow* window, int operation, ...);
+ static int hook_query(const ANativeWindow* window, int what, int* value);
+ static int hook_setSwapInterval(ANativeWindow* window, int interval);
+
+ static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+ static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer);
+ static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+ static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+};
+
+}; // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 9a6df75..4be8bd9 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -472,8 +472,9 @@
window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) {
ColorMode colorMode = surface->mColorMode;
+ sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
destroySurface(surface);
- *surfaceOut = createSurface(window, colorMode);
+ *surfaceOut = createSurface(window, colorMode, colorSpace);
surface = *surfaceOut;
}
@@ -647,7 +648,7 @@
imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType
- : kRGBA_8888_SkColorType, nullptr, &props);
+ : kRGBA_8888_SkColorType, surface->mColorSpace, &props);
}
SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -744,7 +745,7 @@
surface->mWindowWidth = extent.width;
surface->mWindowHeight = extent.height;
- uint32_t imageCount = caps.minImageCount + 2;
+ uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount);
if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
// Application must settle for fewer images than desired:
imageCount = caps.maxImageCount;
@@ -830,17 +831,23 @@
createBuffers(surface, surfaceFormat, extent);
+ // The window content is not updated (frozen) until a buffer of the window size is received.
+ // This prevents temporary stretching of the window after it is resized, but before the first
+ // buffer with new size is enqueued.
+ native_window_set_scaling_mode(surface->mNativeWindow, NATIVE_WINDOW_SCALING_MODE_FREEZE);
+
return true;
}
-VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode) {
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
+ sk_sp<SkColorSpace> surfaceColorSpace) {
initialize();
if (!window) {
return nullptr;
}
- VulkanSurface* surface = new VulkanSurface(colorMode, window);
+ VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 8594a1b..d67d2c8 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -38,8 +38,8 @@
class VulkanSurface {
public:
- VulkanSurface(ColorMode colorMode, ANativeWindow* window)
- : mColorMode(colorMode), mNativeWindow(window) {}
+ VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace)
+ : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace) {}
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
@@ -79,6 +79,7 @@
ANativeWindow* mNativeWindow;
int mWindowWidth = 0;
int mWindowHeight = 0;
+ sk_sp<SkColorSpace> mColorSpace;
};
// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -96,7 +97,8 @@
// Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
// VulkanSurface object which is returned.
- VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode);
+ VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
+ sk_sp<SkColorSpace> surfaceColorSpace);
// Destroy the VulkanSurface and all associated vulkan objects.
void destroySurface(VulkanSurface* surface);
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 8a1bc4d..f812022 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -81,7 +81,7 @@
auto utf16 = asciiToUtf16(text);
uint32_t length = strlen(text);
SkPaint glyphPaint(paint);
- glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawText(
utf16.get(), length, // text buffer
0, length, // draw range
@@ -94,7 +94,7 @@
const SkPath& path) {
auto utf16 = asciiToUtf16(text);
SkPaint glyphPaint(paint);
- glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
nullptr);
}
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 89f0c52..146662b 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -30,18 +30,6 @@
void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
ADD_FAILURE() << "onDrawDRRect not expected in this test";
}
- void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
- const SkPaint& paint) {
- ADD_FAILURE() << "onDrawText not expected in this test";
- }
- void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
- const SkPaint& paint) {
- ADD_FAILURE() << "onDrawPosText not expected in this test";
- }
- void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY,
- const SkPaint& paint) {
- ADD_FAILURE() << "onDrawPosTextH not expected in this test";
- }
void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
const SkRect* cullRect, const SkPaint& paint) {
ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
diff --git a/libs/incident/proto/android/os/metadata.proto b/libs/incident/proto/android/os/metadata.proto
index f8f4e36..3b0e9c9 100644
--- a/libs/incident/proto/android/os/metadata.proto
+++ b/libs/incident/proto/android/os/metadata.proto
@@ -61,8 +61,10 @@
optional bool timed_out = 7;
// true if the section is truncated.
optional bool is_truncated = 8;
+ // message for debugging if there is an error.
+ optional string error_msg = 9;
- // Next Tag: 9
+ // Next Tag: 10;
}
repeated SectionStats sections = 6;
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index fbc21e5..26261ef 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -18,7 +18,7 @@
#include <android/os/IncidentReportArgs.h>
-#include <cutils/log.h>
+#include <log/log.h>
namespace android {
namespace os {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index f1d9397..89d3cc4 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -14,7 +14,6 @@
cc_library_shared {
name: "libinputservice",
-
srcs: [
"PointerController.cpp",
"SpriteController.cpp",
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0a90f85..80d8e72 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,10 +89,6 @@
mLocked.animationPending = false;
- mLocked.displayWidth = -1;
- mLocked.displayHeight = -1;
- mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
-
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
@@ -110,15 +106,6 @@
mLocked.lastFrameUpdatedTime = 0;
mLocked.buttonState = 0;
-
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-
- loadResources();
-
- if (mLocked.pointerIcon.isValid()) {
- mLocked.pointerIconChanged = true;
- updatePointerLocked();
- }
}
PointerController::~PointerController() {
@@ -144,23 +131,15 @@
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
- if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
+
+ if (!mLocked.viewport.isValid()) {
return false;
}
- *outMinX = 0;
- *outMinY = 0;
- switch (mLocked.displayOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
- *outMaxX = mLocked.displayHeight - 1;
- *outMaxY = mLocked.displayWidth - 1;
- break;
- default:
- *outMaxX = mLocked.displayWidth - 1;
- *outMaxY = mLocked.displayHeight - 1;
- break;
- }
+ *outMinX = mLocked.viewport.logicalLeft;
+ *outMinY = mLocked.viewport.logicalTop;
+ *outMaxX = mLocked.viewport.logicalRight - 1;
+ *outMaxY = mLocked.viewport.logicalBottom - 1;
return true;
}
@@ -231,6 +210,12 @@
*outY = mLocked.pointerY;
}
+int32_t PointerController::getDisplayId() const {
+ AutoMutex _l(mLock);
+
+ return mLocked.viewport.displayId;
+}
+
void PointerController::fade(Transition transition) {
AutoMutex _l(mLock);
@@ -355,48 +340,57 @@
void PointerController::reloadPointerResources() {
AutoMutex _l(mLock);
- loadResources();
-
- if (mLocked.presentation == PRESENTATION_POINTER) {
- mLocked.additionalMouseResources.clear();
- mLocked.animationResources.clear();
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
- &mLocked.animationResources);
- }
-
- mLocked.presentationChanged = true;
+ loadResourcesLocked();
updatePointerLocked();
}
-void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
- AutoMutex _l(mLock);
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+ if (viewport.orientation == DISPLAY_ORIENTATION_90
+ || viewport.orientation == DISPLAY_ORIENTATION_270) {
+ width = viewport.deviceHeight;
+ height = viewport.deviceWidth;
+ } else {
+ width = viewport.deviceWidth;
+ height = viewport.deviceHeight;
+ }
+}
- // Adjust to use the display's unrotated coordinate frame.
- if (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270) {
- int32_t temp = height;
- height = width;
- width = temp;
+void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+ AutoMutex _l(mLock);
+ if (viewport == mLocked.viewport) {
+ return;
}
- if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
- mLocked.displayWidth = width;
- mLocked.displayHeight = height;
+ const DisplayViewport oldViewport = mLocked.viewport;
+ mLocked.viewport = viewport;
+
+ int32_t oldDisplayWidth, oldDisplayHeight;
+ getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+ int32_t newDisplayWidth, newDisplayHeight;
+ getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+ // Reset cursor position to center if size or display changed.
+ if (oldViewport.displayId != viewport.displayId
+ || oldDisplayWidth != newDisplayWidth
+ || oldDisplayHeight != newDisplayHeight) {
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
mLocked.pointerX = (minX + maxX) * 0.5f;
mLocked.pointerY = (minY + maxY) * 0.5f;
+ // Reload icon resources for density may be changed.
+ loadResourcesLocked();
} else {
mLocked.pointerX = 0;
mLocked.pointerY = 0;
}
fadeOutAndReleaseAllSpotsLocked();
- }
-
- if (mLocked.displayOrientation != orientation) {
+ } else if (oldViewport.orientation != viewport.orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
@@ -405,37 +399,37 @@
float temp;
// Undo the previous rotation.
- switch (mLocked.displayOrientation) {
+ switch (oldViewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
- x = mLocked.displayWidth - y;
+ x = oldViewport.deviceHeight - y;
y = temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = oldViewport.deviceWidth - x;
+ y = oldViewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = y;
- y = mLocked.displayHeight - temp;
+ y = oldViewport.deviceWidth - temp;
break;
}
// Perform the new rotation.
- switch (orientation) {
+ switch (viewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
- y = mLocked.displayWidth - temp;
+ y = viewport.deviceHeight - temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - x;
+ y = viewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
- x = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - y;
y = temp;
break;
}
@@ -444,7 +438,6 @@
// and save the results.
mLocked.pointerX = x - 0.5f;
mLocked.pointerY = y - 0.5f;
- mLocked.displayOrientation = orientation;
}
updatePointerLocked();
@@ -614,11 +607,16 @@
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
}
-void PointerController::updatePointerLocked() {
+void PointerController::updatePointerLocked() REQUIRES(mLock) {
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
+
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+ mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -729,8 +727,18 @@
}
}
-void PointerController::loadResources() {
+void PointerController::loadResourcesLocked() REQUIRES(mLock) {
mPolicy->loadPointerResources(&mResources);
+
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ mLocked.additionalMouseResources.clear();
+ mLocked.animationResources.clear();
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources);
+ }
+
+ mLocked.pointerIconChanged = true;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 7f4e5a5..a32cc42 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,6 +23,7 @@
#include <vector>
#include <ui/DisplayInfo.h>
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
@@ -96,6 +97,7 @@
virtual int32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
+ virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
@@ -106,7 +108,7 @@
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
+ void setDisplayViewport(const DisplayViewport& viewport);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
@@ -156,9 +158,7 @@
size_t animationFrameIndex;
nsecs_t lastFrameUpdatedTime;
- int32_t displayWidth;
- int32_t displayHeight;
- int32_t displayOrientation;
+ DisplayViewport viewport;
InactivityTimeout inactivityTimeout;
@@ -182,7 +182,7 @@
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
- } mLocked;
+ } mLocked GUARDED_BY(mLock);
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
- void loadResources();
+ void loadResourcesLocked();
};
} // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index eb2bc98..c1868d3 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,13 +144,16 @@
}
}
- // Resize sprites if needed.
+ // Resize and/or reparent sprites if needed.
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
+ if (update.state.surfaceControl == nullptr) {
+ continue;
+ }
- if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+ if (update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
@@ -170,6 +173,12 @@
}
}
}
+
+ // If surface is a new one, we have to set right layer stack.
+ if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
+ t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+ needApplyTransaction = true;
+ }
}
if (needApplyTransaction) {
t.apply();
@@ -236,7 +245,7 @@
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -445,6 +454,15 @@
}
}
+void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.displayId != displayId) {
+ mLocked.state.displayId = displayId;
+ invalidateLocked(DIRTY_DISPLAY_ID);
+ }
+}
+
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 31e43e9..5b216f5 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,6 +125,9 @@
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+ /* Sets the id of the display where the sprite should be shown. */
+ virtual void setDisplayId(int32_t displayId) = 0;
};
/*
@@ -170,6 +173,7 @@
DIRTY_LAYER = 1 << 4,
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
+ DIRTY_DISPLAY_ID = 1 << 7,
};
/* Describes the state of a sprite.
@@ -180,7 +184,7 @@
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
- positionX(0), positionY(0), layer(0), alpha(1.0f),
+ positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
@@ -193,6 +197,7 @@
int32_t layer;
float alpha;
SpriteTransformationMatrix transformationMatrix;
+ int32_t displayId;
sp<SurfaceControl> surfaceControl;
int32_t surfaceWidth;
@@ -225,6 +230,7 @@
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+ virtual void setDisplayId(int32_t displayId);
inline const SpriteState& getStateLocked() const {
return mLocked.state;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index ae87998..32c7520 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -96,9 +96,7 @@
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
void setTestProviderLocation(String provider, in Location loc, String opPackageName);
- void clearTestProviderLocation(String provider, String opPackageName);
void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
- void clearTestProviderEnabled(String provider, String opPackageName);
// --- deprecated ---
void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
@@ -108,12 +106,8 @@
// --- internal ---
- // Used by location providers to tell the location manager when it has a new location.
- // Passive is true if the location is coming from the passive provider, in which case
- // it need not be shared with other providers.
+ // --- deprecated ---
void reportLocation(in Location location, boolean passive);
-
- // Used when a (initially Gnss) Location batch arrives
void reportLocationBatch(in List<Location> locations);
// for reporting callback completion
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3bf98b3..334170e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1537,14 +1537,11 @@
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated This function has always been a no-op, and may be removed in the future.
*/
- public void clearTestProviderLocation(String provider) {
- try {
- mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ @Deprecated
+ public void clearTestProviderLocation(String provider) {}
/**
* Sets a mock enabled value for the given provider. This value will be used in place
@@ -1575,13 +1572,12 @@
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
*/
+ @Deprecated
public void clearTestProviderEnabled(String provider) {
- try {
- mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setTestProviderEnabled(provider, true);
}
/**
diff --git a/location/java/android/location/SettingInjectorService.java b/location/java/android/location/SettingInjectorService.java
index fcd2cde..c201770 100644
--- a/location/java/android/location/SettingInjectorService.java
+++ b/location/java/android/location/SettingInjectorService.java
@@ -17,6 +17,7 @@
package android.location;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
@@ -26,7 +27,7 @@
import android.util.Log;
/**
- * Dynamically specifies the enabled status of a preference injected into
+ * Dynamically specifies the summary (subtitle) and enabled status of a preference injected into
* the list of app settings displayed by the system settings app
* <p/>
* For use only by apps that are included in the system image, for preferences that affect multiple
@@ -71,12 +72,13 @@
* </ul>
*
* To ensure a good user experience, your {@link android.app.Application#onCreate()},
- * and {@link #onGetEnabled()} methods must all be fast. If either is slow,
- * it can delay the display of settings values for other apps as well. Note further that these
- * methods are called on your app's UI thread.
+ * {@link #onGetSummary()}, and {@link #onGetEnabled()} methods must all be fast. If any are slow,
+ * it can delay the display of settings values for other apps as well. Note further that all are
+ * called on your app's UI thread.
* <p/>
* For compactness, only one copy of a given setting should be injected. If each account has a
- * distinct value for the setting, then only {@code settingsActivity} should display the value for
+ * distinct value for the setting, then the {@link #onGetSummary()} value should represent a summary
+ * of the state across all of the accounts and {@code settingsActivity} should display the value for
* each account.
*/
public abstract class SettingInjectorService extends Service {
@@ -108,6 +110,14 @@
"android.location.InjectedSettingChanged";
/**
+ * Name of the bundle key for the string specifying the summary for the setting (e.g., "ON" or
+ * "OFF").
+ *
+ * @hide
+ */
+ public static final String SUMMARY_KEY = "summary";
+
+ /**
* Name of the bundle key for the string specifying whether the setting is currently enabled.
*
* @hide
@@ -150,36 +160,41 @@
}
private void onHandleIntent(Intent intent) {
-
- boolean enabled;
+ String summary = null;
+ boolean enabled = false;
try {
+ summary = onGetSummary();
enabled = onGetEnabled();
- } catch (RuntimeException e) {
- // Exception. Send status anyway, so that settings injector can immediately start
- // loading the status of the next setting.
- sendStatus(intent, true);
- throw e;
+ } finally {
+ // If exception happens, send status anyway, so that settings injector can immediately
+ // start loading the status of the next setting. But leave the exception uncaught to
+ // crash the injector service itself.
+ sendStatus(intent, summary, enabled);
}
-
- sendStatus(intent, enabled);
}
/**
* Send the enabled values back to the caller via the messenger encoded in the
* intent.
*/
- private void sendStatus(Intent intent, boolean enabled) {
+ private void sendStatus(Intent intent, String summary, boolean enabled) {
+ Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY);
+ // Bail out to avoid crashing GmsCore with incoming malicious Intent.
+ if (messenger == null) {
+ return;
+ }
+
Message message = Message.obtain();
Bundle bundle = new Bundle();
+ bundle.putString(SUMMARY_KEY, summary);
bundle.putBoolean(ENABLED_KEY, enabled);
message.setData(bundle);
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, mName + ": received " + intent
+ Log.d(TAG, mName + ": received " + intent + ", summary=" + summary
+ ", enabled=" + enabled + ", sending message: " + message);
}
- Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY);
try {
messenger.send(message);
} catch (RemoteException e) {
@@ -188,14 +203,12 @@
}
/**
- * This method is no longer called, because status values are no longer shown for any injected
- * setting.
+ * Returns the {@link android.preference.Preference#getSummary()} value (allowed to be null or
+ * empty). Should not perform unpredictably-long operations such as network access--see the
+ * running-time comments in the class-level javadoc.
*
- * @return ignored
- *
- * @deprecated not called any more
+ * @return the {@link android.preference.Preference#getSummary()} value
*/
- @Deprecated
protected abstract String onGetSummary();
/**
@@ -217,4 +230,12 @@
* @return the {@link android.preference.Preference#isEnabled()} value
*/
protected abstract boolean onGetEnabled();
+
+ /**
+ * Sends a broadcast to refresh the injected settings on location settings page.
+ */
+ public static final void refreshSettings(Context context) {
+ Intent intent = new Intent(ACTION_INJECTED_SETTING_CHANGED);
+ context.sendBroadcast(intent);
+ }
}
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 39c2d92..71b54fb 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -16,29 +16,26 @@
package com.android.internal.location;
-import android.location.Location;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.WorkSource;
-import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderRequest;
/**
- * Binder interface for services that implement location providers.
- * <p>Use {@link LocationProviderBase} as a helper to implement this
- * interface.
+ * Binder interface for services that implement location providers. Do not implement this directly,
+ * extend {@link LocationProviderBase} instead.
* @hide
*/
interface ILocationProvider {
- void enable();
- void disable();
- void setRequest(in ProviderRequest request, in WorkSource ws);
+ oneway void setLocationProviderManager(in ILocationProviderManager manager);
- // --- deprecated (but still supported) ---
- ProviderProperties getProperties();
+ oneway void setRequest(in ProviderRequest request, in WorkSource ws);
+
+ oneway void sendExtraCommand(String command, in Bundle extras);
+
+ // --- deprecated and will be removed the future ---
int getStatus(out Bundle extras);
long getStatusUpdateTime();
- boolean sendExtraCommand(String command, inout Bundle extras);
}
diff --git a/location/java/android/location/IGnssStatusProvider.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
similarity index 61%
rename from location/java/android/location/IGnssStatusProvider.aidl
rename to location/java/com/android/internal/location/ILocationProviderManager.aidl
index 006b5d3..b1b8f0c 100644
--- a/location/java/android/location/IGnssStatusProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -14,16 +14,21 @@
* limitations under the License.
*/
-package android.location;
+package com.android.internal.location;
-import android.location.IGnssStatusListener;
+import android.location.Location;
+
+import com.android.internal.location.ProviderProperties;
/**
- * An interface for location providers that provide GNSS status information.
- *
- * {@hide}
+ * Binder interface for manager of all location providers.
+ * @hide
*/
-interface IGnssStatusProvider {
- void registerGnssStatusCallback(IGnssStatusListener callback);
- void unregisterGnssStatusCallback(IGnssStatusListener callback);
+interface ILocationProviderManager {
+
+ void onSetEnabled(boolean enabled);
+
+ void onSetProperties(in ProviderProperties properties);
+
+ void onReportLocation(in Location location);
}
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 88919f6..a45c20d 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,15 +16,15 @@
package com.android.internal.location;
-import java.util.ArrayList;
-import java.util.List;
-
import android.annotation.UnsupportedAppUsage;
import android.location.LocationRequest;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.TimeUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/** @hide */
public final class ProviderRequest implements Parcelable {
/** Location reporting is requested (true) */
@@ -36,6 +36,13 @@
public long interval = Long.MAX_VALUE;
/**
+ * When this flag is true, providers should ignore all location settings, user consents, power
+ * restrictions or any other restricting factors and always satisfy this request to the best of
+ * their ability. This flag should only be used in event of an emergency.
+ */
+ public boolean forceLocation = false;
+
+ /**
* Whether provider shall make stronger than normal tradeoffs to substantially restrict power
* use.
*/
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index d19559e..10c3447 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -8,14 +8,18 @@
public abstract class LocationProviderBase {
ctor public LocationProviderBase(java.lang.String, com.android.location.provider.ProviderPropertiesUnbundled);
method public android.os.IBinder getBinder();
- method public abstract void onDisable();
- method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
- method public abstract void onEnable();
- method public deprecated int onGetStatus(android.os.Bundle);
- method public deprecated long onGetStatusUpdateTime();
- method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
- method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
- method public final void reportLocation(android.location.Location);
+ method public boolean isEnabled();
+ method protected deprecated void onDisable();
+ method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method protected deprecated void onEnable();
+ method protected deprecated int onGetStatus(android.os.Bundle);
+ method protected deprecated long onGetStatusUpdateTime();
+ method protected void onInit();
+ method protected boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
+ method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+ method public void reportLocation(android.location.Location);
+ method public void setEnabled(boolean);
+ method public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
field public static final java.lang.String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
field public static final java.lang.String FUSED_PROVIDER = "fused";
}
@@ -38,6 +42,7 @@
}
public final class ProviderRequestUnbundled {
+ method public boolean getForceLocation();
method public long getInterval();
method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
method public boolean getReportLocation();
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index d45a4ba..5bcec92 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,6 +16,7 @@
package com.android.location.provider;
+import android.annotation.Nullable;
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
@@ -29,12 +30,11 @@
import android.util.Log;
import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.PrintWriter;
/**
@@ -55,12 +55,6 @@
* of this package for more information.
*/
public abstract class LocationProviderBase {
- private final String TAG;
-
- /** @hide */
- protected final ILocationManager mLocationManager;
- private final ProviderProperties mProperties;
- private final IBinder mBinder;
/**
* Bundle key for a version of the location containing no GPS data.
@@ -77,49 +71,34 @@
*/
public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
- private final class Service extends ILocationProvider.Stub {
- @Override
- public void enable() {
- onEnable();
- }
- @Override
- public void disable() {
- onDisable();
- }
- @Override
- public void setRequest(ProviderRequest request, WorkSource ws) {
- onSetRequest(new ProviderRequestUnbundled(request), ws);
- }
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
- @Override
- public int getStatus(Bundle extras) {
- return onGetStatus(extras);
- }
- @Override
- public long getStatusUpdateTime() {
- return onGetStatusUpdateTime();
- }
- @Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return onSendExtraCommand(command, extras);
- }
- @Override
- public void dump(FileDescriptor fd, String[] args) {
- PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
- onDump(fd, pw, args);
- pw.flush();
- }
- }
+ private final String mTag;
+ private final IBinder mBinder;
+
+ /**
+ * This field may be removed in the future, do not rely on it.
+ *
+ * @deprecated Do not use this field! Use LocationManager APIs instead. If you use this field
+ * you may be broken in the future.
+ * @hide
+ */
+ @Deprecated
+ protected final ILocationManager mLocationManager;
+
+ // write locked on mBinder, read lock is optional depending on atomicity requirements
+ @Nullable private volatile ILocationProviderManager mManager;
+ private volatile ProviderProperties mProperties;
+ private volatile boolean mEnabled;
public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
- TAG = tag;
- IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
- mLocationManager = ILocationManager.Stub.asInterface(b);
- mProperties = properties.getProviderProperties();
+ mTag = tag;
mBinder = new Service();
+
+ mLocationManager = ILocationManager.Stub.asInterface(
+ ServiceManager.getService(Context.LOCATION_SERVICE));
+
+ mManager = null;
+ mProperties = properties.getProviderProperties();
+ mEnabled = true;
}
public IBinder getBinder() {
@@ -127,51 +106,116 @@
}
/**
- * Used by the location provider to report new locations.
+ * Sets whether this provider is currently enabled or not. Note that this is specific to the
+ * provider only, and is not related to global location settings. This is a hint to the Location
+ * Manager that this provider will generally be unable to fulfill incoming requests. This
+ * provider may still receive callbacks to onSetRequest while not enabled, and must decide
+ * whether to attempt to satisfy those requests or not.
*
- * @param location new Location to report
- *
- * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+ * Some guidelines: providers should set their own enabled/disabled status based only on state
+ * "owned" by that provider. For instance, providers should not take into account the state of
+ * the location master setting when setting themselves enabled or disabled, as this state is not
+ * owned by a particular provider. If a provider requires some additional user consent that is
+ * particular to the provider, this should be use to set the enabled/disabled state. If the
+ * provider proxies to another provider, the child provider's enabled/disabled state should be
+ * taken into account in the parent's enabled/disabled state. For most providers, it is expected
+ * that they will be always enabled.
*/
- public final void reportLocation(Location location) {
- try {
- mLocationManager.reportLocation(location, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException", e);
- } catch (Exception e) {
- // never crash provider, might be running in a system process
- Log.e(TAG, "Exception", e);
+ public void setEnabled(boolean enabled) {
+ synchronized (mBinder) {
+ if (mEnabled == enabled) {
+ return;
+ }
+
+ mEnabled = enabled;
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetEnabled(mEnabled);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
}
}
/**
- * Enable the location provider.
- * <p>The provider may initialize resources, but does
- * not yet need to report locations.
+ * Sets the provider properties that may be queried by clients. Generally speaking, providers
+ * should try to avoid changing their properties after construction.
*/
- public abstract void onEnable();
+ public void setProperties(ProviderPropertiesUnbundled properties) {
+ synchronized (mBinder) {
+ mProperties = properties.getProviderProperties();
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetProperties(mProperties);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
/**
- * Disable the location provider.
- * <p>The provider must release resources, and stop
- * performing work. It may no longer report locations.
+ * Returns true if this provider has been set as enabled. This will be true unless explicitly
+ * set otherwise.
*/
- public abstract void onDisable();
+ public boolean isEnabled() {
+ return mEnabled;
+ }
/**
- * Set the {@link ProviderRequest} requirements for this provider.
- * <p>Each call to this method overrides all previous requests.
- * <p>This method might trigger the provider to start returning
- * locations, or to stop returning locations, depending on the
- * parameters in the request.
+ * Reports a new location from this provider.
*/
- public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
+ public void reportLocation(Location location) {
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onReportLocation(location);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
+
+ protected void onInit() {
+ // call once so that providers designed for APIs pre-Q are not broken
+ onEnable();
+ }
+
+ /**
+ * @deprecated This callback will be invoked once when the provider is created to maintain
+ * backwards compatibility with providers not designed for Android Q and above. This method
+ * should only be implemented in location providers that need to support SDKs below Android Q.
+ * Even in this case, it is usually unnecessary to implement this callback with the correct
+ * design. This method may be removed in the future.
+ */
+ @Deprecated
+ protected void onEnable() {}
+
+ /**
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. Even in
+ * this case, it is usually unnecessary to implement this callback with the correct design. This
+ * method may be removed in the future.
+ */
+ @Deprecated
+ protected void onDisable() {}
+
+ /**
+ * Set the {@link ProviderRequest} requirements for this provider. Each call to this method
+ * overrides all previous requests. This method might trigger the provider to start returning
+ * locations, or to stop returning locations, depending on the parameters in the request.
+ */
+ protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
/**
* Dump debug information.
*/
- public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- }
+ protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {}
/**
* This method will no longer be invoked.
@@ -187,10 +231,12 @@
* <p>If extras is non-null, additional status information may be
* added to it in the form of provider-specific key/value pairs.
*
- * @deprecated This method will no longer be invoked.
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. This
+ * method may be removed in the future.
*/
@Deprecated
- public int onGetStatus(Bundle extras) {
+ protected int onGetStatus(Bundle extras) {
return LocationProvider.AVAILABLE;
}
@@ -206,24 +252,64 @@
*
* @return time of last status update in millis since last reboot
*
- * @deprecated This method will no longer be invoked.
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. This
+ * method may be removed in the future.
*/
@Deprecated
- public long onGetStatusUpdateTime() {
+ protected long onGetStatusUpdateTime() {
return 0;
}
/**
- * Implements addditional location provider specific additional commands.
- *
- * @param command name of the command to send to the provider.
- * @param extras optional arguments for the command (or null).
- * The provider may optionally fill the extras Bundle with results from the command.
- *
- * @return true if the command succeeds.
+ * Implements location provider specific custom commands. The return value will be ignored on
+ * Android Q and above.
*/
- public boolean onSendExtraCommand(String command, Bundle extras) {
- // default implementation
+ protected boolean onSendExtraCommand(@Nullable String command, @Nullable Bundle extras) {
return false;
}
+
+ private final class Service extends ILocationProvider.Stub {
+
+ @Override
+ public void setLocationProviderManager(ILocationProviderManager manager) {
+ synchronized (mBinder) {
+ try {
+ manager.onSetProperties(mProperties);
+ manager.onSetEnabled(mEnabled);
+ } catch (RemoteException e) {
+ Log.w(mTag, e);
+ }
+
+ mManager = manager;
+ }
+
+ onInit();
+ }
+
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource ws) {
+ onSetRequest(new ProviderRequestUnbundled(request), ws);
+ }
+
+ @Override
+ public int getStatus(Bundle extras) {
+ return onGetStatus(extras);
+ }
+
+ @Override
+ public long getStatusUpdateTime() {
+ return onGetStatusUpdateTime();
+ }
+
+ @Override
+ public void sendExtraCommand(String command, Bundle extras) {
+ onSendExtraCommand(command, extras);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ onDump(fd, pw, args);
+ }
+ }
}
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 6a8e618..b825b58 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -16,13 +16,13 @@
package com.android.location.provider;
-import java.util.ArrayList;
-import java.util.List;
-
import android.location.LocationRequest;
import com.android.internal.location.ProviderRequest;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class is an interface to Provider Requests for unbundled applications.
*
@@ -46,6 +46,10 @@
return mRequest.interval;
}
+ public boolean getForceLocation() {
+ return mRequest.forceLocation;
+ }
+
/**
* Never null.
*/
diff --git a/media/Android.bp b/media/Android.bp
new file mode 100644
index 0000000..d5da6f2
--- /dev/null
+++ b/media/Android.bp
@@ -0,0 +1,36 @@
+java_library {
+ // TODO: include media2.jar in the media apex and add it to the bootclasspath.
+ name: "media2",
+
+ srcs: [
+ ":media2-srcs",
+ ":framework-media-annotation-srcs",
+ ],
+
+ static_libs: [
+ "mediaplayer2-protos",
+ ],
+
+ // Make sure that the implementaion only relies on SDK or system APIs.
+ sdk_version: "system_current",
+}
+
+filegroup {
+ name: "media2-srcs",
+ srcs: [
+ "java/android/media/CloseGuard.java",
+ "java/android/media/DataSourceCallback.java",
+ "java/android/media/DataSourceDesc.java",
+ "java/android/media/UriDataSourceDesc.java",
+ "java/android/media/FileDataSourceDesc.java",
+ "java/android/media/CallbackDataSourceDesc.java",
+ "java/android/media/VideoSize.java",
+ "java/android/media/Media2Utils.java",
+ "java/android/media/MediaPlayer2Utils.java",
+ "java/android/media/MediaPlayer2.java",
+ "java/android/media/Media2HTTPService.java",
+ "java/android/media/Media2HTTPConnection.java",
+ "java/android/media/RoutingDelegate.java",
+ "java/android/media/BufferingParams.java",
+ ],
+}
diff --git a/media/OWNERS b/media/OWNERS
index 0abf9ae..03b751c 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,12 +1,13 @@
+chz@google.com
+dwkang@google.com
elaurent@google.com
etalvala@google.com
gkasten@google.com
hunga@google.com
+jaewan@google.com
jmtrivi@google.com
+jsharkey@android.com
lajos@google.com
marcone@google.com
sungsoo@google.com
wjia@google.com
-jaewan@google.com
-chz@google.com
-dwkang@google.com
diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java
index 5467a69..0a9ca02 100644
--- a/media/java/android/media/AudioFocusInfo.java
+++ b/media/java/android/media/AudioFocusInfo.java
@@ -80,16 +80,12 @@
* The audio attributes for the audio focus request.
* @return non-null {@link AudioAttributes}.
*/
- @SystemApi
public AudioAttributes getAttributes() { return mAttributes; }
- @SystemApi
public int getClientUid() { return mClientUid; }
- @SystemApi
public String getClientId() { return mClientId; }
- @SystemApi
public String getPackageName() { return mPackageName; }
/**
@@ -99,7 +95,6 @@
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK},
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
*/
- @SystemApi
public int getGainRequest() { return mGainRequest; }
/**
@@ -109,7 +104,6 @@
* {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT} or
* {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
*/
- @SystemApi
public int getLossReceived() { return mLossReceived; }
/** @hide */
@@ -124,7 +118,6 @@
* {@link AudioManager#AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and
* {@link AudioManager#AUDIOFOCUS_FLAG_LOCK}.
*/
- @SystemApi
public int getFlags() { return mFlags; }
@Override
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 3e3e651..793aa27 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -213,6 +213,8 @@
* (e.g.{@link AudioTrack#getPlaybackHeadPosition()
* AudioTrack.getPlaybackHeadPosition()}),
* depending on the context where audio frame is used.
+ * For the purposes of {@link AudioFormat#getFrameSizeInBytes()}, a compressed data format
+ * returns a frame size of 1 byte.
*/
public final class AudioFormat implements Parcelable {
@@ -670,27 +672,53 @@
}
/**
- * Private constructor with an ignored argument to differentiate from the removed default ctor
- * @param ignoredArgument
- */
- private AudioFormat(int ignoredArgument) {
- }
-
- /**
* Constructor used by the JNI. Parameters are not checked for validity.
*/
// Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this
// constructor
@UnsupportedAppUsage
private AudioFormat(int encoding, int sampleRate, int channelMask, int channelIndexMask) {
- mEncoding = encoding;
- mSampleRate = sampleRate;
- mChannelMask = channelMask;
- mChannelIndexMask = channelIndexMask;
- mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING |
- AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE |
- AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK |
- AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK;
+ this(
+ AUDIO_FORMAT_HAS_PROPERTY_ENCODING
+ | AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE
+ | AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK
+ | AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK,
+ encoding, sampleRate, channelMask, channelIndexMask
+ );
+ }
+
+ private AudioFormat(int propertySetMask,
+ int encoding, int sampleRate, int channelMask, int channelIndexMask) {
+ mPropertySetMask = propertySetMask;
+ mEncoding = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0
+ ? encoding : ENCODING_INVALID;
+ mSampleRate = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0
+ ? sampleRate : SAMPLE_RATE_UNSPECIFIED;
+ mChannelMask = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0
+ ? channelMask : CHANNEL_INVALID;
+ mChannelIndexMask = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0
+ ? channelIndexMask : CHANNEL_INVALID;
+
+ // Compute derived values.
+
+ final int channelIndexCount = Integer.bitCount(getChannelIndexMask());
+ int channelCount = channelCountFromOutChannelMask(getChannelMask());
+ if (channelCount == 0) {
+ channelCount = channelIndexCount;
+ } else if (channelCount != channelIndexCount && channelIndexCount != 0) {
+ channelCount = 0; // position and index channel count mismatch
+ }
+ mChannelCount = channelCount;
+
+ int frameSizeInBytes = 1;
+ try {
+ frameSizeInBytes = getBytesPerSample(mEncoding) * channelCount;
+ } catch (IllegalArgumentException iae) {
+ // ignored
+ }
+ // it is possible that channel count is 0, so ensure we return 1 for
+ // mFrameSizeInBytes for consistency.
+ mFrameSizeInBytes = frameSizeInBytes != 0 ? frameSizeInBytes : 1;
}
/** @hide */
@@ -704,14 +732,21 @@
/** @hide */
public final static int AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK = 0x1 << 3;
+ // This is an immutable class, all member variables are final.
+
+ // Essential values.
@UnsupportedAppUsage
- private int mEncoding;
+ private final int mEncoding;
@UnsupportedAppUsage
- private int mSampleRate;
+ private final int mSampleRate;
@UnsupportedAppUsage
- private int mChannelMask;
- private int mChannelIndexMask;
- private int mPropertySetMask;
+ private final int mChannelMask;
+ private final int mChannelIndexMask;
+ private final int mPropertySetMask;
+
+ // Derived values computed in the constructor, cached here.
+ private final int mChannelCount;
+ private final int mFrameSizeInBytes;
/**
* Return the encoding.
@@ -721,9 +756,6 @@
* {@link AudioFormat#ENCODING_INVALID} if not set.
*/
public int getEncoding() {
- if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_ENCODING) == 0) {
- return ENCODING_INVALID;
- }
return mEncoding;
}
@@ -745,9 +777,6 @@
* {@link AudioFormat#CHANNEL_INVALID} if not set.
*/
public int getChannelMask() {
- if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) == 0) {
- return CHANNEL_INVALID;
- }
return mChannelMask;
}
@@ -760,9 +789,6 @@
* {@link AudioFormat#CHANNEL_INVALID} if not set or an invalid mask was used.
*/
public int getChannelIndexMask() {
- if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) == 0) {
- return CHANNEL_INVALID;
- }
return mChannelIndexMask;
}
@@ -772,14 +798,26 @@
* Zero is returned if both the channel position mask and the channel index mask are not set.
*/
public int getChannelCount() {
- final int channelIndexCount = Integer.bitCount(getChannelIndexMask());
- int channelCount = channelCountFromOutChannelMask(getChannelMask());
- if (channelCount == 0) {
- channelCount = channelIndexCount;
- } else if (channelCount != channelIndexCount && channelIndexCount != 0) {
- channelCount = 0; // position and index channel count mismatch
- }
- return channelCount;
+ return mChannelCount;
+ }
+
+ /**
+ * Return the frame size in bytes.
+ *
+ * For PCM or PCM packed compressed data this is the size of a sample multiplied
+ * by the channel count. For all other cases, including invalid/unset channel masks,
+ * this will return 1 byte.
+ * As an example, a stereo 16-bit PCM format would have a frame size of 4 bytes,
+ * an 8 channel float PCM format would have a frame size of 32 bytes,
+ * and a compressed data format (not packed in PCM) would have a frame size of 1 byte.
+ *
+ * Both {@link AudioRecord} or {@link AudioTrack} process data in multiples of
+ * this frame size.
+ *
+ * @return The audio frame size in bytes corresponding to the encoding and the channel mask.
+ */
+ public int getFrameSizeInBytes() {
+ return mFrameSizeInBytes;
}
/** @hide */
@@ -790,7 +828,7 @@
/** @hide */
public String toLogFriendlyString() {
return String.format("%dch %dHz %s",
- getChannelCount(), mSampleRate, toLogFriendlyEncoding(mEncoding));
+ mChannelCount, mSampleRate, toLogFriendlyEncoding(mEncoding));
}
/**
@@ -839,14 +877,13 @@
* @return a new {@link AudioFormat} object
*/
public AudioFormat build() {
- AudioFormat af = new AudioFormat(1980/*ignored*/);
- af.mEncoding = mEncoding;
- // not calling setSampleRate is equivalent to calling
- // setSampleRate(SAMPLE_RATE_UNSPECIFIED)
- af.mSampleRate = mSampleRate;
- af.mChannelMask = mChannelMask;
- af.mChannelIndexMask = mChannelIndexMask;
- af.mPropertySetMask = mPropertySetMask;
+ AudioFormat af = new AudioFormat(
+ mPropertySetMask,
+ mEncoding,
+ mSampleRate,
+ mChannelMask,
+ mChannelIndexMask
+ );
return af;
}
@@ -1049,11 +1086,13 @@
}
private AudioFormat(Parcel in) {
- mPropertySetMask = in.readInt();
- mEncoding = in.readInt();
- mSampleRate = in.readInt();
- mChannelMask = in.readInt();
- mChannelIndexMask = in.readInt();
+ this(
+ in.readInt(), // propertySetMask
+ in.readInt(), // encoding
+ in.readInt(), // sampleRate
+ in.readInt(), // channelMask
+ in.readInt() // channelIndexMask
+ );
}
public static final Parcelable.Creator<AudioFormat> CREATOR =
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d10900e..da52a26 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5152,6 +5152,15 @@
return reportedSurroundFormats;
}
+ /**
+ * Return if audio haptic coupled playback is supported or not.
+ *
+ * @return whether audio haptic playback supported.
+ */
+ public static boolean isHapticPlaybackSupported() {
+ return AudioSystem.isHapticPlaybackSupported();
+ }
+
//---------------------------------------------------------
// Inner classes
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 823af65..fca7074 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -254,6 +254,22 @@
mLabels.hashCode());
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getSimpleName() + " ");
+ sb.append("{ presentation id=" + mPresentationId);
+ sb.append(", program id=" + mProgramId);
+ sb.append(", language=" + mLanguage);
+ sb.append(", labels=" + mLabels);
+ sb.append(", mastering indication=" + mMasteringIndication);
+ sb.append(", audio description=" + mAudioDescriptionAvailable);
+ sb.append(", spoken subtitles=" + mSpokenSubtitlesAvailable);
+ sb.append(", dialogue enhancement=" + mDialogueEnhancementAvailable);
+ sb.append(" }");
+ return sb.toString();
+ }
+
/**
* A builder class for creating {@link AudioPresentation} objects.
*/
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 2a575b6..4b2353c 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,16 +16,6 @@
package android.media;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.ArrayList;
-import java.util.List;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -39,13 +29,21 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
/**
* The AudioRecord class manages the audio resources for Java applications
* to record audio from the audio input hardware of the platform. This is
@@ -1807,6 +1805,8 @@
private native final int native_get_active_microphones(
ArrayList<MicrophoneInfo> activeMicrophones);
+ private native int native_getPortId();
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 082a375..36f635a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -933,6 +933,11 @@
*/
public static native int setA11yServicesUids(int[] uids);
+ /**
+ * @see AudioManager#isHapticPlaybackSupported()
+ */
+ public static native boolean isHapticPlaybackSupported();
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 71736dd..5e77fdf 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -995,6 +995,35 @@
}
}
+ /**
+ * Returns whether direct playback of an audio format with the provided attributes is
+ * currently supported on the system.
+ * <p>Direct playback means that the audio stream is not resampled or downmixed
+ * by the framework. Checking for direct support can help the app select the representation
+ * of audio content that most closely matches the capabilities of the device and peripherials
+ * (e.g. A/V receiver) connected to it. Note that the provided stream can still be re-encoded
+ * or mixed with other streams, if needed.
+ * <p>Also note that this query only provides information about the support of an audio format.
+ * It does not indicate whether the resources necessary for the playback are available
+ * at that instant.
+ * @param format a non-null {@link AudioFormat} instance describing the format of
+ * the audio data.
+ * @param attributes a non-null {@link AudioAttributes} instance.
+ * @return true if the given audio format can be played directly.
+ */
+ public static boolean isDirectPlaybackSupported(@NonNull AudioFormat format,
+ @NonNull AudioAttributes attributes) {
+ if (format == null) {
+ throw new IllegalArgumentException("Illegal null AudioFormat argument");
+ }
+ if (attributes == null) {
+ throw new IllegalArgumentException("Illegal null AudioAttributes argument");
+ }
+ return native_is_direct_output_supported(format.getEncoding(), format.getSampleRate(),
+ format.getChannelMask(), format.getChannelIndexMask(),
+ attributes.getContentType(), attributes.getUsage(), attributes.getFlags());
+ }
+
// mask of all the positional channels supported, however the allowed combinations
// are further restricted by the matching left/right rule and
// AudioSystem.OUT_CHANNEL_COUNT_MAX
@@ -3328,6 +3357,9 @@
// Native methods called from the Java side
//--------------------
+ private static native boolean native_is_direct_output_supported(int encoding, int sampleRate,
+ int channelMask, int channelIndexMask, int contentType, int usage, int flags);
+
// post-condition: mStreamType is overwritten with a value
// that reflects the audio attributes (e.g. an AudioAttributes object with a usage of
// AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC
@@ -3365,7 +3397,7 @@
int offsetInFloats, int sizeInFloats, int format,
boolean isBlocking);
- private native final int native_write_native_bytes(Object audioData,
+ private native final int native_write_native_bytes(ByteBuffer audioData,
int positionInBytes, int sizeInBytes, int format, boolean blocking);
private native final int native_reload_static();
@@ -3423,6 +3455,8 @@
private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
private native final int native_setPresentation(int presentationId, int programId);
+ private native int native_getPortId();
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java
index 0e8e6ce..cd36414 100644
--- a/media/java/android/media/CallbackDataSourceDesc.java
+++ b/media/java/android/media/CallbackDataSourceDesc.java
@@ -19,13 +19,10 @@
import android.annotation.NonNull;
/**
- * @hide
* Structure of data source descriptor for sources using callback.
*
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
- * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
- * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
- * to set data source for playback.
+ * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
+ * {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}.
*
@@ -38,7 +35,6 @@
/**
* Return the DataSourceCallback of this data source.
- * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}.
* @return the DataSourceCallback of this data source
*/
public DataSourceCallback getDataSourceCallback() {
diff --git a/media/java/android/media/CloseGuard.java b/media/java/android/media/CloseGuard.java
new file mode 100644
index 0000000..2014673
--- /dev/null
+++ b/media/java/android/media/CloseGuard.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.util.Log;
+
+/**
+ * Note: This file is copied from dalvik.system package with the following modifications:
+ * - Remove @CorePlatformApi, @IntraCoreApi and @UnsupportedAppUsage annotations.
+ * - Replace System.logW() with android.util.Log.w().
+ * This file should be used only within media mainline module.
+ * TODO: Remove this file and use dalvik.system.CloseGuard once
+ * @CorePlatformApi becomes stable or we have a replacement in SDK API.
+ * b/120419300
+ *
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre> {@code
+ * class Foo {
+ *
+ * {@literal @}ReachabilitySensitive
+ * private final CloseGuard guard = CloseGuard.get();
+ *
+ * ...
+ *
+ * public Foo() {
+ * ...;
+ * guard.open("cleanup");
+ * }
+ *
+ * public void cleanup() {
+ * guard.close();
+ * ...;
+ * }
+ *
+ * protected void finalize() throws Throwable {
+ * try {
+ * // Note that guard could be null if the constructor threw.
+ * if (guard != null) {
+ * guard.warnIfOpen();
+ * }
+ * cleanup();
+ * } finally {
+ * super.finalize();
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up is
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre> {@code
+ * class Bar {
+ *
+ * {@literal @}ReachabilitySensitive
+ * private final CloseGuard guard = CloseGuard.get();
+ *
+ * ...
+ *
+ * public Bar() {
+ * ...;
+ * }
+ *
+ * public void connect() {
+ * ...;
+ * guard.open("cleanup");
+ * }
+ *
+ * public void cleanup() {
+ * guard.close();
+ * ...;
+ * }
+ *
+ * protected void finalize() throws Throwable {
+ * try {
+ * // Note that guard could be null if the constructor threw.
+ * if (guard != null) {
+ * guard.warnIfOpen();
+ * }
+ * cleanup();
+ * } finally {
+ * super.finalize();
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * When used in a constructor, calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ *
+ * The @ReachabilitySensitive annotation ensures that finalize() cannot be
+ * called during the explicit call to cleanup(), prior to the guard.close call.
+ * There is an extremely small chance that, for code that neglects to call
+ * cleanup(), finalize() and thus cleanup() will be called while a method on
+ * the object is still active, but the "this" reference is no longer required.
+ * If missing cleanup() calls are expected, additional @ReachabilitySensitive
+ * annotations or reachabilityFence() calls may be required.
+ *
+ * @hide
+ */
+final class CloseGuard {
+
+ /**
+ * True if collection of call-site information (the expensive operation
+ * here) and tracking via a Tracker (see below) are enabled.
+ * Enabled by default so we can diagnose issues early in VM startup.
+ * Note, however, that Android disables this early in its startup,
+ * but enables it with DropBoxing for system apps on debug builds.
+ */
+ private static volatile boolean stackAndTrackingEnabled = true;
+
+ /**
+ * Hook for customizing how CloseGuard issues are reported.
+ * Bypassed if stackAndTrackingEnabled was false when open was called.
+ */
+ private static volatile Reporter reporter = new DefaultReporter();
+
+ /**
+ * Hook for customizing how CloseGuard issues are tracked.
+ */
+ private static volatile Tracker currentTracker = null; // Disabled by default.
+
+ /**
+ * Returns a CloseGuard instance. {@code #open(String)} can be used to set
+ * up the instance to warn on failure to close.
+ */
+ public static CloseGuard get() {
+ return new CloseGuard();
+ }
+
+ /**
+ * Enables/disables stack capture and tracking. A call stack is captured
+ * during open(), and open/close events are reported to the Tracker, only
+ * if enabled is true. If a stack trace was captured, the {@link
+ * #getReporter() reporter} is informed of unclosed resources; otherwise a
+ * one-line warning is logged.
+ */
+ public static void setEnabled(boolean enabled) {
+ CloseGuard.stackAndTrackingEnabled = enabled;
+ }
+
+ /**
+ * True if CloseGuard stack capture and tracking are enabled.
+ */
+ public static boolean isEnabled() {
+ return stackAndTrackingEnabled;
+ }
+
+ /**
+ * Used to replace default Reporter used to warn of CloseGuard
+ * violations when stack tracking is enabled. Must be non-null.
+ */
+ public static void setReporter(Reporter rep) {
+ if (rep == null) {
+ throw new NullPointerException("reporter == null");
+ }
+ CloseGuard.reporter = rep;
+ }
+
+ /**
+ * Returns non-null CloseGuard.Reporter.
+ */
+ public static Reporter getReporter() {
+ return reporter;
+ }
+
+ /**
+ * Sets the {@link Tracker} that is notified when resources are allocated and released.
+ * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
+ * was called. A null argument disables tracking.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ */
+ public static void setTracker(Tracker tracker) {
+ currentTracker = tracker;
+ }
+
+ /**
+ * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
+ * there is none.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ */
+ public static Tracker getTracker() {
+ return currentTracker;
+ }
+
+ private CloseGuard() {}
+
+ /**
+ * {@code open} initializes the instance with a warning that the caller
+ * should have explicitly called the {@code closer} method instead of
+ * relying on finalization.
+ *
+ * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
+ * @throws NullPointerException if closer is null.
+ */
+ public void open(String closer) {
+ // always perform the check for valid API usage...
+ if (closer == null) {
+ throw new NullPointerException("closer == null");
+ }
+ // ...but avoid allocating an allocation stack if "disabled"
+ if (!stackAndTrackingEnabled) {
+ closerNameOrAllocationInfo = closer;
+ return;
+ }
+ String message = "Explicit termination method '" + closer + "' not called";
+ Throwable stack = new Throwable(message);
+ closerNameOrAllocationInfo = stack;
+ Tracker tracker = currentTracker;
+ if (tracker != null) {
+ tracker.open(stack);
+ }
+ }
+
+ // We keep either an allocation stack containing the closer String or, when
+ // in disabled state, just the closer String.
+ // We keep them in a single field only to minimize overhead.
+ private Object /* String or Throwable */ closerNameOrAllocationInfo;
+
+ /**
+ * Marks this CloseGuard instance as closed to avoid warnings on
+ * finalization.
+ */
+ public void close() {
+ Tracker tracker = currentTracker;
+ if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) {
+ // Invoke tracker on close only if we invoked it on open. Tracker may have changed.
+ tracker.close((Throwable) closerNameOrAllocationInfo);
+ }
+ closerNameOrAllocationInfo = null;
+ }
+
+ /**
+ * Logs a warning if the caller did not properly cleanup by calling an
+ * explicit close method before finalization. If CloseGuard was enabled
+ * when the CloseGuard was created, passes the stacktrace associated with
+ * the allocation to the current reporter. If it was not enabled, it just
+ * directly logs a brief message.
+ */
+ public void warnIfOpen() {
+ if (closerNameOrAllocationInfo != null) {
+ if (closerNameOrAllocationInfo instanceof String) {
+ Log.w("CloseGuard", "A resource failed to call "
+ + (String) closerNameOrAllocationInfo + ". ");
+ } else {
+ String message =
+ "A resource was acquired at attached stack trace but never released. ";
+ message += "See java.io.Closeable for information on avoiding resource leaks.";
+ Throwable stack = (Throwable) closerNameOrAllocationInfo;
+ reporter.report(message, stack);
+ }
+ }
+ }
+
+ /**
+ * Interface to allow customization of tracking behaviour.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ */
+ public interface Tracker {
+ void open(Throwable allocationSite);
+ void close(Throwable allocationSite);
+ }
+
+ /**
+ * Interface to allow customization of reporting behavior.
+ * @hide
+ */
+ public interface Reporter {
+ void report(String message, Throwable allocationSite);
+ }
+
+ /**
+ * Default Reporter which reports CloseGuard violations to the log.
+ */
+ private static final class DefaultReporter implements Reporter {
+ private DefaultReporter() {}
+
+ @Override public void report (String message, Throwable allocationSite) {
+ Log.w("CloseGuard", message, allocationSite);
+ }
+ }
+}
diff --git a/media/java/android/media/DataSourceCallback.java b/media/java/android/media/DataSourceCallback.java
index 9b27baf..0d4d531 100644
--- a/media/java/android/media/DataSourceCallback.java
+++ b/media/java/android/media/DataSourceCallback.java
@@ -21,7 +21,6 @@
import java.io.IOException;
/**
- * @hide
* For supplying media data to the framework. Implement this if your app has
* special requirements for the way media data is obtained.
*
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index 702034e..7fc6f79 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -19,13 +19,10 @@
import android.annotation.NonNull;
/**
- * @hide
* Base class of data source descriptor.
*
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
- * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
- * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
- * to set data source for playback.
+ * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
+ * {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use subclasses' builder to change {@link DataSourceDesc}.
*
@@ -36,6 +33,9 @@
// keep consistent with native code
public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
+ /**
+ * @hide
+ */
public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000;
public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS;
@@ -172,7 +172,8 @@
/**
* Sets the end position in milliseconds at which the playback will end.
- * Any negative number is treated as maximum length of the data source.
+ * Any negative number is treated as maximum duration {@link #LONG_MAX_TIME_MS}
+ * of the data source
*
* @param position the end position in milliseconds at which the playback will end
* @return the same Builder instance.
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index 763a81f..aca8dbe 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -23,13 +23,10 @@
import java.io.IOException;
/**
- * @hide
* Structure of data source descriptor for sources using file descriptor.
*
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
- * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
- * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
- * to set data source for playback.
+ * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
+ * {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
*
@@ -166,9 +163,9 @@
* Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
* created by this builder is passed to {@link MediaPlayer2} via
- * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
- * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
- * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+ * {@link MediaPlayer2#setDataSource},
+ * {@link MediaPlayer2#setNextDataSource} or
+ * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
* close the ParcelFileDescriptor.
*
* @param pfd the ParcelFileDescriptor for the file to play
@@ -186,9 +183,9 @@
* Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
* created by this builder is passed to {@link MediaPlayer2} via
- * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
- * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
- * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+ * {@link MediaPlayer2#setDataSource},
+ * {@link MediaPlayer2#setNextDataSource} or
+ * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
* close the ParcelFileDescriptor.
*
* Any negative number for offset is treated as 0.
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 1d27c03..0375de3 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -27,7 +27,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.IHwBinder;
import android.os.Looper;
import android.os.Message;
@@ -1427,6 +1426,24 @@
<td>⎆</td>
</tr>
<tr>
+ <td>(29+)</td>
+ <td>29+</td>
+ <td>29+</td>
+ <td>29+</td>
+ <td>(29+)</td>
+ <td>(29+)</td>
+ <td>-</td>
+ <td class=fn>{@link #setAudioPresentation setAudioPresentation}</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
<td>-</td>
<td>-</td>
<td>18+</td>
@@ -3260,6 +3277,19 @@
public native final void setVideoScalingMode(@VideoScalingMode int mode);
/**
+ * Sets the audio presentation.
+ * @param presentation see {@link AudioPresentation}. In particular, id should be set.
+ */
+ public void setAudioPresentation(@NonNull AudioPresentation presentation) {
+ if (presentation == null) {
+ throw new IllegalArgumentException("audio presentation is null");
+ }
+ native_setAudioPresentation(presentation.getPresentationId(), presentation.getProgramId());
+ }
+
+ private native void native_setAudioPresentation(int presentationId, int programId);
+
+ /**
* Get the component name. If the codec was created by createDecoderByType
* or createEncoderByType, what component is chosen is not known beforehand.
* @throws IllegalStateException if in the Released state.
@@ -3308,6 +3338,55 @@
public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
/**
+ * Set the HDR10+ metadata on the next queued input frame.
+ *
+ * Provide a byte array of data that's conforming to the
+ * user_data_registered_itu_t_t35() syntax of SEI message for ST 2094-40.
+ *<p>
+ * For decoders:
+ *<p>
+ * When a decoder is configured for one of the HDR10+ profiles that uses
+ * out-of-band metadata (such as {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus} or {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}), this
+ * parameter sets the HDR10+ metadata on the next input buffer queued
+ * to the decoder. A decoder supporting these profiles must propagate
+ * the metadata to the format of the output buffer corresponding to this
+ * particular input buffer (under key {@link MediaFormat#KEY_HDR10_PLUS_INFO}).
+ * The metadata should be applied to that output buffer and the buffers
+ * following it (in display order), until the next output buffer (in
+ * display order) upon which an HDR10+ metadata is set.
+ *<p>
+ * This parameter shouldn't be set if the decoder is not configured for
+ * an HDR10+ profile that uses out-of-band metadata. In particular,
+ * it shouldn't be set for HDR10+ profiles that uses in-band metadata
+ * where the metadata is embedded in the input buffers, for example
+ * {@link MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}.
+ *<p>
+ * For encoders:
+ *<p>
+ * When an encoder is configured for one of the HDR10+ profiles and the
+ * operates in byte buffer input mode (instead of surface input mode),
+ * this parameter sets the HDR10+ metadata on the next input buffer queued
+ * to the encoder. For the HDR10+ profiles that uses out-of-band metadata
+ * (such as {@link MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus},
+ * or {@link MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}),
+ * the metadata must be propagated to the format of the output buffer
+ * corresponding to this particular input buffer (under key {@link
+ * MediaFormat#KEY_HDR10_PLUS_INFO}). For the HDR10+ profiles that uses
+ * in-band metadata (such as {@link
+ * MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}), the
+ * metadata info must be embedded in the corresponding output buffer itself.
+ *<p>
+ * This parameter shouldn't be set if the encoder is not configured for
+ * an HDR10+ profile, or if it's operating in surface input mode.
+ *<p>
+ *
+ * @see MediaFormat#KEY_HDR10_PLUS_INFO
+ */
+ public static final String PARAMETER_KEY_HDR10_PLUS_INFO = MediaFormat.KEY_HDR10_PLUS_INFO;
+
+ /**
* Communicate additional parameter changes to the component instance.
* <b>Note:</b> Some of these parameter changes may silently fail to apply.
*
@@ -3325,7 +3404,14 @@
int i = 0;
for (final String key: params.keySet()) {
keys[i] = key;
- values[i] = params.get(key);
+ Object value = params.get(key);
+
+ // Bundle's byte array is a byte[], JNI layer only takes ByteBuffer
+ if (value instanceof byte[]) {
+ values[i] = ByteBuffer.wrap((byte[])value);
+ } else {
+ values[i] = value;
+ }
++i;
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 6301993..9025829 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1135,6 +1135,10 @@
maxChannels = 6;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
maxChannels = 16;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3_JOC)) {
+ sampleRates = new int[] { 48000 };
+ bitRates = Range.create(32000, 6144000);
+ maxChannels = 16;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
sampleRates = new int[] { 44100, 48000, 96000, 192000 };
bitRates = Range.create(16000, 2688000);
@@ -2516,6 +2520,8 @@
case CodecProfileLevel.VP9Profile3:
case CodecProfileLevel.VP9Profile2HDR:
case CodecProfileLevel.VP9Profile3HDR:
+ case CodecProfileLevel.VP9Profile2HDR10Plus:
+ case CodecProfileLevel.VP9Profile3HDR10Plus:
break;
default:
Log.w(TAG, "Unrecognized profile "
@@ -2608,7 +2614,9 @@
switch (profileLevel.profile) {
case CodecProfileLevel.HEVCProfileMain:
case CodecProfileLevel.HEVCProfileMain10:
+ case CodecProfileLevel.HEVCProfileMainStill:
case CodecProfileLevel.HEVCProfileMain10HDR10:
+ case CodecProfileLevel.HEVCProfileMain10HDR10Plus:
break;
default:
Log.w(TAG, "Unrecognized profile "
@@ -2999,6 +3007,8 @@
// HDR profiles also support passing HDR metadata
public static final int VP9Profile2HDR = 0x1000;
public static final int VP9Profile3HDR = 0x2000;
+ public static final int VP9Profile2HDR10Plus = 0x4000;
+ public static final int VP9Profile3HDR10Plus = 0x8000;
// from OMX_VIDEO_VP9LEVELTYPE
public static final int VP9Level1 = 0x1;
@@ -3021,6 +3031,7 @@
public static final int HEVCProfileMain10 = 0x02;
public static final int HEVCProfileMainStill = 0x04;
public static final int HEVCProfileMain10HDR10 = 0x1000;
+ public static final int HEVCProfileMain10HDR10Plus = 0x2000;
// from OMX_VIDEO_HEVCLEVELTYPE
public static final int HEVCMainTierLevel1 = 0x1;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 284e422..594a224 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -17,20 +17,29 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.util.AbstractSet;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
- * Encapsulates the information describing the format of media data,
- * be it audio or video.
- *
- * The format of the media data is specified as string/value pairs.
- *
+ * Encapsulates the information describing the format of media data, be it audio or video, as
+ * well as optional feature metadata.
+ * <p>
+ * The format of the media data is specified as key/value pairs. Keys are strings. Values can
+ * be integer, long, float, String or ByteBuffer.
+ * <p>
+ * The feature metadata is specificed as string/boolean pairs.
+ * <p>
* Keys common to all audio/video formats, <b>all keys not marked optional are mandatory</b>:
*
* <table>
@@ -138,6 +147,7 @@
public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+ public static final String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
@@ -901,6 +911,27 @@
public static final String KEY_HDR_STATIC_INFO = "hdr-static-info";
/**
+ * An optional key describing the HDR10+ metadata of the video content.
+ *
+ * The associated value is a ByteBuffer containing HDR10+ metadata conforming to the
+ * user_data_registered_itu_t_t35() syntax of SEI message for ST 2094-40. This key will
+ * be present on:
+ *<p>
+ * - The formats of output buffers of a decoder configured for HDR10+ profiles (such as
+ * {@link MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus}, {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus} or {@link
+ * MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}), or
+ *<p>
+ * - The formats of output buffers of an encoder configured for an HDR10+ profiles that
+ * uses out-of-band metadata (such as {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus} or {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}).
+ *
+ * @see MediaCodec#PARAMETER_KEY_HDR10_PLUS_INFO
+ */
+ public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
+
+ /**
* A key describing a unique ID for the content of a media track.
*
* <p>This key is used by {@link MediaExtractor}. Some extractors provide multiple encodings
@@ -938,7 +969,6 @@
*/
public static final String KEY_CA_SESSION_ID = "ca-session-id";
-
/**
* A key describing the private data in the CA_descriptor associated with a media track.
* <p>
@@ -950,7 +980,7 @@
*/
public static final String KEY_CA_PRIVATE_DATA = "ca-private-data";
- /* package private */ MediaFormat(Map<String, Object> map) {
+ /* package private */ MediaFormat(@NonNull Map<String, Object> map) {
mMap = map;
}
@@ -969,11 +999,58 @@
/**
* Returns true iff a key of the given name exists in the format.
*/
- public final boolean containsKey(String name) {
+ public final boolean containsKey(@NonNull String name) {
return mMap.containsKey(name);
}
/**
+ * Returns true iff a feature of the given name exists in the format.
+ */
+ public final boolean containsFeature(@NonNull String name) {
+ return mMap.containsKey(KEY_FEATURE_ + name);
+ }
+
+ public static final int TYPE_NULL = 0;
+ public static final int TYPE_INTEGER = 1;
+ public static final int TYPE_LONG = 2;
+ public static final int TYPE_FLOAT = 3;
+ public static final int TYPE_STRING = 4;
+ public static final int TYPE_BYTE_BUFFER = 5;
+
+ /** @hide */
+ @IntDef({
+ TYPE_NULL,
+ TYPE_INTEGER,
+ TYPE_LONG,
+ TYPE_FLOAT,
+ TYPE_STRING,
+ TYPE_BYTE_BUFFER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /**
+ * Returns the value type for a key. If the key does not exist, it returns TYPE_NULL.
+ */
+ public final @Type int getValueTypeForKey(@NonNull String name) {
+ Object value = mMap.get(name);
+ if (value == null) {
+ return TYPE_NULL;
+ } else if (value instanceof Integer) {
+ return TYPE_INTEGER;
+ } else if (value instanceof Long) {
+ return TYPE_LONG;
+ } else if (value instanceof Float) {
+ return TYPE_FLOAT;
+ } else if (value instanceof String) {
+ return TYPE_STRING;
+ } else if (value instanceof ByteBuffer) {
+ return TYPE_BYTE_BUFFER;
+ }
+ throw new RuntimeException("invalid value for key");
+ }
+
+ /**
* A key prefix used together with a {@link MediaCodecInfo.CodecCapabilities}
* feature name describing a required or optional feature for a codec capabilities
* query.
@@ -989,64 +1066,165 @@
public static final String KEY_FEATURE_ = "feature-";
/**
- * Returns the value of an integer key.
+ * Returns the value of a numeric key. This is provided as a convenience method for keys
+ * that may take multiple numeric types, such as {@link #KEY_FRAME_RATE}, or {@link
+ * #KEY_I_FRAME_INTERVAL}.
+ *
+ * @return null if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is ByteBuffer or String
*/
- public final int getInteger(String name) {
+ public final @Nullable Number getNumber(@NonNull String name) {
+ return ((Number)mMap.get(name));
+ }
+
+ /**
+ * Returns the value of a numeric key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is ByteBuffer or String
+ */
+ public final @NonNull Number getNumber(@NonNull String name, @NonNull Number defaultValue) {
+ Number ret = getNumber(name);
+ return ret == null ? defaultValue : ret;
+ }
+
+ /**
+ * Returns the value of an integer key.
+ *
+ * @throws NullPointerException if the key does not exist or the stored value for the key is
+ * null
+ * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+ * String
+ */
+ public final int getInteger(@NonNull String name) {
return ((Integer)mMap.get(name)).intValue();
}
/**
- * Returns the value of an integer key, or the default value if the
- * key is missing or is for another type value.
- * @hide
+ * Returns the value of an integer key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+ * String
*/
- public final int getInteger(String name, int defaultValue) {
+ public final int getInteger(@NonNull String name, int defaultValue) {
try {
return getInteger(name);
+ } catch (NullPointerException e) {
+ /* no such field or field is null */
+ return defaultValue;
}
- catch (NullPointerException e) { /* no such field */ }
- catch (ClassCastException e) { /* field of different type */ }
- return defaultValue;
}
/**
* Returns the value of a long key.
+ *
+ * @throws NullPointerException if the key does not exist or the stored value for the key is
+ * null
+ * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+ * String
*/
- public final long getLong(String name) {
+ public final long getLong(@NonNull String name) {
return ((Long)mMap.get(name)).longValue();
}
/**
- * Returns the value of a float key.
+ * Returns the value of an long key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+ * String
*/
- public final float getFloat(String name) {
+ public final long getLong(@NonNull String name, long defaultValue) {
+ try {
+ return getLong(name);
+ } catch (NullPointerException e) {
+ /* no such field or field is null */
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a float key.
+ *
+ * @throws NullPointerException if the key does not exist or the stored value for the key is
+ * null
+ * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+ * String
+ */
+ public final float getFloat(@NonNull String name) {
return ((Float)mMap.get(name)).floatValue();
}
/**
- * Returns the value of a string key.
+ * Returns the value of an float key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+ * String
*/
- public final String getString(String name) {
+ public final float getFloat(@NonNull String name, float defaultValue) {
+ try {
+ return getFloat(name);
+ } catch (NullPointerException e) {
+ /* no such field or field is null */
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a string key.
+ *
+ * @return null if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
+ */
+ public final @Nullable String getString(@NonNull String name) {
return (String)mMap.get(name);
}
/**
- * Returns the value of a ByteBuffer key.
+ * Returns the value of an string key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
*/
- public final ByteBuffer getByteBuffer(String name) {
+ public final @NonNull String getString(@NonNull String name, @NonNull String defaultValue) {
+ String ret = getString(name);
+ return ret == null ? defaultValue : ret;
+ }
+
+ /**
+ * Returns the value of a ByteBuffer key.
+ *
+ * @return null if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or String
+ */
+ public final @Nullable ByteBuffer getByteBuffer(@NonNull String name) {
return (ByteBuffer)mMap.get(name);
}
/**
+ * Returns the value of a ByteBuffer key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or String
+ */
+ public final @NonNull ByteBuffer getByteBuffer(
+ @NonNull String name, @NonNull ByteBuffer defaultValue) {
+ ByteBuffer ret = getByteBuffer(name);
+ return ret == null ? defaultValue : ret;
+ }
+
+ /**
* Returns whether a feature is to be enabled ({@code true}) or disabled
* ({@code false}).
*
* @param feature the name of a {@link MediaCodecInfo.CodecCapabilities} feature.
*
* @throws IllegalArgumentException if the feature was neither set to be enabled
- * nor to be disabled.
+ * nor to be disabled.
*/
- public boolean getFeatureEnabled(String feature) {
+ public boolean getFeatureEnabled(@NonNull String feature) {
Integer enabled = (Integer)mMap.get(KEY_FEATURE_ + feature);
if (enabled == null) {
throw new IllegalArgumentException("feature is not specified");
@@ -1057,39 +1235,239 @@
/**
* Sets the value of an integer key.
*/
- public final void setInteger(String name, int value) {
+ public final void setInteger(@NonNull String name, int value) {
mMap.put(name, Integer.valueOf(value));
}
/**
* Sets the value of a long key.
*/
- public final void setLong(String name, long value) {
+ public final void setLong(@NonNull String name, long value) {
mMap.put(name, Long.valueOf(value));
}
/**
* Sets the value of a float key.
*/
- public final void setFloat(String name, float value) {
+ public final void setFloat(@NonNull String name, float value) {
mMap.put(name, new Float(value));
}
/**
* Sets the value of a string key.
+ * <p>
+ * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+ * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+ * remove a key.
*/
- public final void setString(String name, String value) {
+ public final void setString(@NonNull String name, @Nullable String value) {
mMap.put(name, value);
}
/**
* Sets the value of a ByteBuffer key.
+ * <p>
+ * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+ * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+ * remove a key.
*/
- public final void setByteBuffer(String name, ByteBuffer bytes) {
+ public final void setByteBuffer(@NonNull String name, @Nullable ByteBuffer bytes) {
mMap.put(name, bytes);
}
/**
+ * Removes a value of a given key if present. Has no effect if the key is not present.
+ */
+ public final void removeKey(@NonNull String name) {
+ // exclude feature mappings
+ if (!name.startsWith(KEY_FEATURE_)) {
+ mMap.remove(name);
+ }
+ }
+
+ /**
+ * Removes a given feature setting if present. Has no effect if the feature setting is not
+ * present.
+ */
+ public final void removeFeature(@NonNull String name) {
+ mMap.remove(KEY_FEATURE_ + name);
+ }
+
+ /**
+ * A Partial set view for a portion of the keys in a MediaFormat object.
+ *
+ * This class is needed as we want to return a portion of the actual format keys in getKeys()
+ * and another portion of the keys in getFeatures(), and still allow the view properties.
+ */
+ private abstract class FilteredMappedKeySet extends AbstractSet<String> {
+ private Set<String> mKeys;
+
+ // Returns true if this set should include this key
+ abstract protected boolean keepKey(String key);
+
+ // Maps a key from the underlying key set into its new value in this key set
+ abstract protected String mapKeyToItem(String key);
+
+ // Maps a key from this key set into its original value in the underlying key set
+ abstract protected String mapItemToKey(String item);
+
+ public FilteredMappedKeySet() {
+ mKeys = mMap.keySet();
+ }
+
+ // speed up contains and remove from abstract implementation (that would iterate
+ // over each element)
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof String) {
+ String key = mapItemToKey((String)o);
+ return keepKey(key) && mKeys.contains(key);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (o instanceof String) {
+ String key = mapItemToKey((String)o);
+ if (keepKey(key) && mKeys.remove(key)) {
+ mMap.remove(key);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class KeyIterator implements Iterator<String> {
+ Iterator<String> mIterator;
+ String mLast;
+
+ public KeyIterator() {
+ // We must create a copy of the filtered stream, as remove operation has to modify
+ // the underlying data structure (mMap), so the iterator's operation is undefined.
+ // Use a list as it is likely less memory consuming than the other alternative: set.
+ mIterator =
+ mKeys.stream().filter(k -> keepKey(k)).collect(Collectors.toList()).iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mIterator.hasNext();
+ }
+
+ @Override
+ public String next() {
+ mLast = mIterator.next();
+ return mapKeyToItem(mLast);
+ }
+
+ @Override
+ public void remove() {
+ mIterator.remove();
+ mMap.remove(mLast);
+ }
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return new KeyIterator();
+ }
+
+ @Override
+ public int size() {
+ return (int)mKeys.stream().filter(k -> keepKey(k)).count();
+ }
+ }
+
+ /**
+ * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+ * don't start with a prefix, such as "feature-"
+ */
+ private class UnprefixedKeySet extends FilteredMappedKeySet {
+ private String mPrefix;
+
+ public UnprefixedKeySet(String prefix) {
+ super();
+ mPrefix = prefix;
+ }
+
+ protected boolean keepKey(String key) {
+ return !key.startsWith(mPrefix);
+ }
+
+ protected String mapKeyToItem(String key) {
+ return key;
+ }
+
+ protected String mapItemToKey(String item) {
+ return item;
+ }
+ }
+
+ /**
+ * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+ * start with a prefix, such as "feature-", with the prefix removed
+ */
+ private class PrefixedKeySetWithPrefixRemoved extends FilteredMappedKeySet {
+ private String mPrefix;
+ private int mPrefixLength;
+
+ public PrefixedKeySetWithPrefixRemoved(String prefix) {
+ super();
+ mPrefix = prefix;
+ mPrefixLength = prefix.length();
+ }
+
+ protected boolean keepKey(String key) {
+ return key.startsWith(mPrefix);
+ }
+
+ protected String mapKeyToItem(String key) {
+ return key.substring(mPrefixLength);
+ }
+
+ protected String mapItemToKey(String item) {
+ return mPrefix + item;
+ }
+ }
+
+
+ /**
+ * Returns a {@link java.util.Set Set} view of the keys contained in this MediaFormat.
+ *
+ * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+ * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+ * (except through the iterator's own remove operation), the results of the iteration are
+ * undefined. The set supports element removal, which removes the corresponding mapping from the
+ * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+ * It does not support the add or addAll operations.
+ */
+ public final @NonNull java.util.Set<String> getKeys() {
+ return new UnprefixedKeySet(KEY_FEATURE_);
+ }
+
+ /**
+ * Returns a {@link java.util.Set Set} view of the features contained in this MediaFormat.
+ *
+ * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+ * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+ * (except through the iterator's own remove operation), the results of the iteration are
+ * undefined. The set supports element removal, which removes the corresponding mapping from the
+ * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+ * It does not support the add or addAll operations.
+ */
+ public final @NonNull java.util.Set<String> getFeatures() {
+ return new PrefixedKeySetWithPrefixRemoved(KEY_FEATURE_);
+ }
+
+ /**
+ * Create a copy of a media format object.
+ */
+ public MediaFormat(@NonNull MediaFormat other) {
+ mMap.putAll(other.mMap);
+ }
+
+ /**
* Sets whether a feature is to be enabled ({@code true}) or disabled
* ({@code false}).
*
@@ -1102,7 +1480,7 @@
* @see MediaCodecList#findEncoderForFormat
* @see MediaCodecInfo.CodecCapabilities#isFormatSupported
*/
- public void setFeatureEnabled(String feature, boolean enabled) {
+ public void setFeatureEnabled(@NonNull String feature, boolean enabled) {
setInteger(KEY_FEATURE_ + feature, enabled ? 1 : 0);
}
@@ -1112,8 +1490,8 @@
* @param sampleRate The sampling rate of the content.
* @param channelCount The number of audio channels in the content.
*/
- public static final MediaFormat createAudioFormat(
- String mime,
+ public static final @NonNull MediaFormat createAudioFormat(
+ @NonNull String mime,
int sampleRate,
int channelCount) {
MediaFormat format = new MediaFormat();
@@ -1132,8 +1510,8 @@
* in the content. (This will also work if there are multiple language
* tracks in the content.)
*/
- public static final MediaFormat createSubtitleFormat(
- String mime,
+ public static final @NonNull MediaFormat createSubtitleFormat(
+ @NonNull String mime,
String language) {
MediaFormat format = new MediaFormat();
format.setString(KEY_MIME, mime);
@@ -1148,8 +1526,8 @@
* @param width The width of the content (in pixels)
* @param height The height of the content (in pixels)
*/
- public static final MediaFormat createVideoFormat(
- String mime,
+ public static final @NonNull MediaFormat createVideoFormat(
+ @NonNull String mime,
int width,
int height) {
MediaFormat format = new MediaFormat();
@@ -1161,7 +1539,7 @@
}
@Override
- public String toString() {
+ public @NonNull String toString() {
return mMap.toString();
}
}
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index c91d4d3..0fb392b 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -18,10 +18,10 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
+
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
@@ -269,8 +269,10 @@
public static final int MUXER_OUTPUT_3GPP = MUXER_OUTPUT_FIRST + 2;
/** HEIF media file format*/
public static final int MUXER_OUTPUT_HEIF = MUXER_OUTPUT_FIRST + 3;
+ /** Ogg media file format*/
+ public static final int MUXER_OUTPUT_OGG = MUXER_OUTPUT_FIRST + 4;
/** @hide */
- public static final int MUXER_OUTPUT_LAST = MUXER_OUTPUT_HEIF;
+ public static final int MUXER_OUTPUT_LAST = MUXER_OUTPUT_OGG;
};
/** @hide */
@@ -279,6 +281,7 @@
OutputFormat.MUXER_OUTPUT_WEBM,
OutputFormat.MUXER_OUTPUT_3GPP,
OutputFormat.MUXER_OUTPUT_HEIF,
+ OutputFormat.MUXER_OUTPUT_OGG,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Format {}
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 111dd0f..d4b1c7f 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -21,9 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -48,8 +45,6 @@
import com.android.framework.protobuf.InvalidProtocolBufferException;
import com.android.internal.annotations.GuardedBy;
-import dalvik.system.CloseGuard;
-
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
@@ -83,8 +78,6 @@
import java.util.concurrent.atomic.AtomicLong;
/**
- * @hide
- *
* MediaPlayer2 class can be used to control playback of audio/video files and streams.
*
* <p>Topics covered here are:
@@ -105,11 +98,10 @@
* <p>The MediaPlayer2 object has five states:</p>
* <ol>
* <li><p>{@link #PLAYER_STATE_IDLE}: MediaPlayer2 is in the <strong>Idle</strong>
- * state after you create it using
- * {@link #create()}, or after calling {@link #reset()}.</p>
+ * state after it's created, or after calling {@link #reset()}.</p>
*
* <p>While in this state, you should call
- * {@link #setDataSource(DataSourceDesc2) setDataSource()}. It is a good
+ * {@link #setDataSource setDataSource}. It is a good
* programming practice to register an {@link EventCallback#onCallCompleted onCallCompleted}
* <a href="#Callbacks">callback</a> and watch for {@link #CALL_STATUS_BAD_VALUE} and
* {@link #CALL_STATUS_ERROR_IO}, which might be caused by <code>setDataSource</code>.
@@ -141,7 +133,7 @@
* while streaming audio/video.</p>
*
* <p> When the playback reaches the end of stream, the behavior depends on whether or
- * not you've enabled looping by calling {@link #loopCurrent(boolean) loopCurrent}:</p>
+ * not you've enabled looping by calling {@link #loopCurrent}:</p>
* <ul>
* <li>If the looping mode was set to <code>false</code>, the player will transfer
* to the <strong>Paused</strong> state. If you registered an {@link EventCallback#onInfo
@@ -168,15 +160,15 @@
* <p>If you register an {@link EventCallback#onError onError}}
* <a href="#Callbacks">callback</a>,
* the callback will be performed when entering the state. When programming errors happen,
- * such as calling {@link #prepare() prepare} and
- * {@link #setDataSource(DataSourceDesc) setDataSource} methods
+ * such as calling {@link #prepare()} and
+ * {@link #setDataSource} methods
* from an <a href="#InvalidStates">invalid state</a>, the callback is called with
* {@link #CALL_STATUS_INVALID_OPERATION}. The MediaPlayer2 object enters the
* <strong>Error</strong> state whether or not a callback exists. </p>
*
* <p>To recover from an error and reuse a MediaPlayer2 object that is in the <strong>
* Error</strong> state,
- * call {@link #reset() reset}. The object will return to the <strong>Idle</strong>
+ * call {@link #reset()}. The object will return to the <strong>Idle</strong>
* state and all state information will be lost.</p>
* </li>
* </ol>
@@ -187,26 +179,26 @@
*
* <li>Use <a href="#Callbacks">callbacks</a> to respond to state changes and errors.</li>
*
- * <li>When a MediaPlayer2 object is no longer being used, call {@link #close() close} as soon as
+ * <li>When a MediaPlayer2 object is no longer being used, call {@link #close()} as soon as
* possible to release the resources used by the internal player engine associated with the
- * MediaPlayer2. Failure to call {@link #close() close} may cause subsequent instances of
+ * MediaPlayer2. Failure to call {@link #close()} may cause subsequent instances of
* MediaPlayer2 objects to fallback to software implementations or fail altogether.
* You cannot use MediaPlayer2
- * after you call {@link #close() close}. There is no way to bring it back to any other state.</li>
+ * after you call {@link #close()}. There is no way to bring it back to any other state.</li>
*
* <li>The current playback position can be retrieved with a call to
- * {@link #getCurrentPosition() getCurrentPosition},
+ * {@link #getCurrentPosition()},
* which is helpful for applications such as a Music player that need to keep track of the playback
* progress.</li>
*
- * <li>The playback position can be adjusted with a call to {@link #seekTo seekTo}. Although the
- * asynchronous {@link #seekTo seekTo} call returns right away, the actual seek operation may take a
+ * <li>The playback position can be adjusted with a call to {@link #seekTo}. Although the
+ * asynchronous {@link #seekTo} call returns right away, the actual seek operation may take a
* while to finish, especially for audio/video being streamed. If you register an
* {@link EventCallback#onCallCompleted onCallCompleted} <a href="#Callbacks">callback</a>,
* the callback is
* called When the seek operation completes with {@link #CALL_COMPLETED_SEEK_TO}.</li>
*
- * <li>You can call {@link #seekTo seekTo} from the <strong>Paused</strong> state.
+ * <li>You can call {@link #seekTo} from the <strong>Paused</strong> state.
* In this case, if you are playing a video stream and
* the requested position is valid one video frame is displayed.</li>
*
@@ -215,13 +207,13 @@
* <h3 id="InvalidStates">Invalid method calls</h3>
*
* <p>The only methods you safely call from the <strong>Error</strong> state are
- * {@link #close() close},
- * {@link #reset() reset},
- * {@link #notifyWhenCommandLabelReached notifyWhenCommandLabelReached},
- * {@link #clearPendingCommands() clearPendingCommands},
- * {@link #setEventCallback setEventCallback},
- * {@link #clearEventCallback() clearEventCallback}
- * and {@link #getState() getState}.
+ * {@link #close},
+ * {@link #reset},
+ * {@link #notifyWhenCommandLabelReached},
+ * {@link #clearPendingCommands},
+ * {@link #registerEventCallback},
+ * {@link #unregisterEventCallback}
+ * and {@link #getState}.
* Any other methods might throw an exception, return meaningless data, or invoke a
* {@link EventCallback#onCallCompleted onCallCompleted} with an error code.</p>
*
@@ -255,8 +247,7 @@
* <h3 id="Callbacks">Callbacks</h3>
* <p>Many errors do not result in a transition to the <strong>Error</strong> state.
* It is good programming practice to register callback listeners using
- * {@link #setEventCallback(Executor, EventCallback) setEventCallback} and
- * {@link #setDrmEventCallback(Executor, DrmEventCallback) setDrmEventCallback}).
+ * {@link #registerEventCallback}.
* You can receive a callback at any time and from any state.</p>
*
* <p>If it's important for your app to respond to state changes (for instance, to update the
@@ -778,7 +769,7 @@
*
* @return the current DataSourceDesc
*/
- public DataSourceDesc getCurrentDataSource() {
+ public @Nullable DataSourceDesc getCurrentDataSource() {
synchronized (mSrcLock) {
return mCurrentSourceInfo == null ? null : mCurrentSourceInfo.mDSD;
}
@@ -1254,19 +1245,18 @@
*
* <p>This function has the MediaPlayer2 access the low-level power manager
* service to control the device's power usage while playing is occurring.
- * The parameter is a combination of {@link android.os.PowerManager} wake flags.
+ * The parameter is a {@link android.os.PowerManager.WakeLock}.
* Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
* permission.
* By default, no attempt is made to keep the device awake during playback.
*
- * @param context the Context to use
- * @param mode the power/wake mode to set
+ * @param wakeLock the power wake lock used during playback.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
* @see android.os.PowerManager
*/
// This is an asynchronous call.
- public Object setWakeMode(Context context, int mode) {
- return addTask(new Task(CALL_COMPLETED_SET_WAKE_MODE, false) {
+ public Object setWakeLock(@NonNull PowerManager.WakeLock wakeLock) {
+ return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) {
@Override
void process() {
boolean washeld = false;
@@ -1276,28 +1266,15 @@
washeld = true;
mWakeLock.release();
}
- mWakeLock = null;
}
- PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- ActivityManager am =
- (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- List<RunningAppProcessInfo> runningAppsProcInfo = am.getRunningAppProcesses();
- int pid = android.os.Process.myPid();
- String name = "pid " + String.valueOf(pid);
- if (runningAppsProcInfo != null) {
- for (RunningAppProcessInfo procInfo : runningAppsProcInfo) {
- if (procInfo.pid == pid) {
- name = procInfo.processName;
- break;
- }
+ mWakeLock = wakeLock;
+ if (mWakeLock != null) {
+ mWakeLock.setReferenceCounted(false);
+ if (washeld) {
+ mWakeLock.acquire();
}
}
- mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, name);
- mWakeLock.setReferenceCounted(false);
- if (washeld) {
- mWakeLock.acquire();
- }
}
});
}
@@ -1305,7 +1282,7 @@
/**
* Control whether we should use the attached SurfaceHolder to keep the
* screen on while video playback is occurring. This is the preferred
- * method over {@link #setWakeMode} where possible, since it doesn't
+ * method over {@link #setWakeLock} where possible, since it doesn't
* require that the application have permission for low-level wake lock
* access.
*
@@ -1352,9 +1329,13 @@
*
* @param token the command to be canceled. This is the returned Object when command is issued.
* @return {@code false} if the task could not be cancelled; {@code true} otherwise.
+ * @throws IllegalArgumentException if argument token is null.
*/
// This is a synchronous call.
- public boolean cancelCommand(Object token) {
+ public boolean cancelCommand(@NonNull Object token) {
+ if (token == null) {
+ throw new IllegalArgumentException("command token should not be null");
+ }
synchronized (mTaskLock) {
return mPendingTasks.remove(token);
}
@@ -1462,7 +1443,7 @@
* @return the size of the video. The width and height of size could be 0 if there is no video,
* no display surface was set, or the size has not been determined yet.
* The {@code EventCallback} can be registered via
- * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * {@link #registerEventCallback(Executor, EventCallback)} to provide a
* notification {@code EventCallback.onVideoSizeChanged} when the size
* is available.
*/
@@ -1730,10 +1711,12 @@
public MediaTimestamp getTimestamp() {
try {
// TODO: get the timestamp from native side
- return new MediaTimestamp(
- getCurrentPosition() * 1000L,
- System.nanoTime(),
- getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f);
+ return new MediaTimestamp.Builder()
+ .setMediaTimestamp(
+ getCurrentPosition() * 1000L,
+ System.nanoTime(),
+ getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f)
+ .build();
} catch (IllegalStateException e) {
return null;
}
@@ -1893,7 +1876,6 @@
* Gets the track type.
* @return TrackType which indicates if the track is video, audio, timed text.
*/
- @UnsupportedAppUsage
public int getTrackType() {
return mTrackType;
}
@@ -1904,7 +1886,6 @@
* When the language is unknown or could not be determined,
* ISO-639-2 language code, "und", is returned.
*/
- @UnsupportedAppUsage
public String getLanguage() {
String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
return language == null ? "und" : language;
@@ -1935,19 +1916,20 @@
final int mTrackType;
final MediaFormat mFormat;
- TrackInfo(Iterator<Value> in) {
- mTrackType = in.next().getInt32Value();
+ static TrackInfo create(Iterator<Value> in) {
+ int trackType = in.next().getInt32Value();
// TODO: build the full MediaFormat; currently we are using createSubtitleFormat
// even for audio/video tracks, meaning we only set the mime and language.
String mime = in.next().getStringValue();
String language = in.next().getStringValue();
- mFormat = MediaFormat.createSubtitleFormat(mime, language);
+ MediaFormat format = MediaFormat.createSubtitleFormat(mime, language);
- if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value());
- mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value());
- mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value());
+ if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
+ format.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value());
+ format.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value());
+ format.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value());
}
+ return new TrackInfo(trackType, format);
}
/** @hide */
@@ -1992,9 +1974,9 @@
* addTimedTextSource method is called.
* @throws IllegalStateException if it is called in an invalid state.
*/
- public List<TrackInfo> getTrackInfo() {
+ public @NonNull List<TrackInfo> getTrackInfo() {
TrackInfo[] trackInfo = getInbandTrackInfo();
- return Arrays.asList(trackInfo);
+ return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0));
}
private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
@@ -2012,7 +1994,7 @@
}
TrackInfo[] trackInfo = new TrackInfo[size];
for (int i = 0; i < size; ++i) {
- trackInfo[i] = new TrackInfo(in);
+ trackInfo[i] = TrackInfo.create(in);
}
return trackInfo;
}
@@ -2418,11 +2400,13 @@
return;
}
Iterator<Value> in = playerMsg.getValuesList().iterator();
- SubtitleData data = new SubtitleData(
- in.next().getInt32Value(), // trackIndex
- in.next().getInt64Value(), // startTimeUs
- in.next().getInt64Value(), // durationUs
- in.next().getBytesValue().toByteArray()); // data
+ SubtitleData data = new SubtitleData.Builder()
+ .setSubtitleData(
+ in.next().getInt32Value(), // trackIndex
+ in.next().getInt64Value(), // startTimeUs
+ in.next().getInt64Value(), // durationUs
+ in.next().getBytesValue().toByteArray()) // data
+ .build();
sendEvent(new EventNotifier() {
@Override
public void notify(EventCallback callback) {
@@ -2446,9 +2430,11 @@
return;
}
Iterator<Value> in = playerMsg.getValuesList().iterator();
- data = new TimedMetaData(
- in.next().getInt64Value(), // timestampUs
- in.next().getBytesValue().toByteArray()); // metaData
+ data = new TimedMetaData.Builder()
+ .setTimedMetaData(
+ in.next().getInt64Value(), // timestampUs
+ in.next().getBytesValue().toByteArray()) // metaData
+ .build();
} else {
data = null;
}
@@ -2507,7 +2493,7 @@
Log.w(TAG, "MEDIA_DRM_INFO failed to parse msg.obj " + obj);
break;
}
- DrmInfo drmInfo = new DrmInfo(playerMsg);
+ DrmInfo drmInfo = DrmInfo.create(playerMsg);
synchronized (sourceInfo) {
sourceInfo.mDrmInfo = drmInfo;
}
@@ -2558,7 +2544,7 @@
* @param size the size of the video
*/
public void onVideoSizeChanged(
- MediaPlayer2 mp, DataSourceDesc dsd, VideoSize size) { }
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull VideoSize size) { }
/**
* Called to indicate an avaliable timed text
@@ -2569,7 +2555,8 @@
* needed to be displayed and the display format.
* @hide
*/
- public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) { }
+ public void onTimedText(
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull TimedText text) { }
/**
* Called to indicate avaliable timed metadata
@@ -2590,7 +2577,8 @@
* @param data the timed metadata sample associated with this event
*/
public void onTimedMetaDataAvailable(
- MediaPlayer2 mp, DataSourceDesc dsd, TimedMetaData data) { }
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @NonNull TimedMetaData data) { }
/**
* Called to indicate an error.
@@ -2602,7 +2590,8 @@
* implementation dependent.
*/
public void onError(
- MediaPlayer2 mp, DataSourceDesc dsd, @MediaError int what, int extra) { }
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @MediaError int what, int extra) { }
/**
* Called to indicate an info or a warning.
@@ -2613,7 +2602,9 @@
* @param extra an extra code, specific to the info. Typically
* implementation dependent.
*/
- public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, @MediaInfo int what, int extra) { }
+ public void onInfo(
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @MediaInfo int what, int extra) { }
/**
* Called to acknowledge an API call.
@@ -2624,7 +2615,7 @@
* @param status the returned status code for the call.
*/
public void onCallCompleted(
- MediaPlayer2 mp, DataSourceDesc dsd, @CallCompleted int what,
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @CallCompleted int what,
@CallStatus int status) { }
/**
@@ -2635,7 +2626,8 @@
* @param timestamp the new media clock.
*/
public void onMediaTimeDiscontinuity(
- MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @NonNull MediaTimestamp timestamp) { }
/**
* Called to indicate {@link #notifyWhenCommandLabelReached(Object)} has been processed.
@@ -2644,7 +2636,7 @@
* @param label the application specific Object given by
* {@link #notifyWhenCommandLabelReached(Object)}.
*/
- public void onCommandLabelReached(MediaPlayer2 mp, @NonNull Object label) { }
+ public void onCommandLabelReached(@NonNull MediaPlayer2 mp, @NonNull Object label) { }
/**
* Called when when a player subtitle track has new subtitle data available.
@@ -2653,7 +2645,8 @@
* @param data the subtitle data
*/
public void onSubtitleData(
- MediaPlayer2 mp, DataSourceDesc dsd, @NonNull SubtitleData data) { }
+ @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @NonNull SubtitleData data) { }
}
private final Object mEventCbLock = new Object();
@@ -3048,10 +3041,10 @@
*/
public static final int CALL_COMPLETED_SET_DISPLAY = 33;
- /** The player just completed a call {@link #setWakeMode}.
+ /** The player just completed a call {@link #setWakeLock}.
* @see EventCallback#onCallCompleted
*/
- public static final int CALL_COMPLETED_SET_WAKE_MODE = 34;
+ public static final int CALL_COMPLETED_SET_WAKE_LOCK = 34;
/** The player just completed a call {@link #setScreenOnWhilePlaying}.
* @see EventCallback#onCallCompleted
@@ -3071,7 +3064,7 @@
public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED =
SEPARATE_CALL_COMPLETED_CALLBACK_START;
- /** The player just completed a call {@link #prepareDrm(DataSourceDesc, UUID)}.
+ /** The player just completed a call {@link #prepareDrm}.
* @see DrmEventCallback#onDrmPrepared
* @hide
*/
@@ -3104,7 +3097,7 @@
CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES,
CALL_COMPLETED_SET_BUFFERING_PARAMS,
CALL_COMPLETED_SET_DISPLAY,
- CALL_COMPLETED_SET_WAKE_MODE,
+ CALL_COMPLETED_SET_WAKE_LOCK,
CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING,
CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED,
CALL_COMPLETED_PREPARE_DRM,
@@ -3149,9 +3142,10 @@
public static final int CALL_STATUS_SKIPPED = 5;
/** Status code represents that DRM operation is called before preparing a DRM scheme through
- * {@link #prepareDrm(DataSourceDesc, UUID)}.
+ * {@code prepareDrm}.
* @see EventCallback#onCallCompleted
*/
+ // TODO: change @code to @link when DRM is unhidden
public static final int CALL_STATUS_NO_DRM_SCHEME = 6;
/**
@@ -3181,6 +3175,7 @@
* The only allowed DRM calls in this listener are
* {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)}
* and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}.
+ * @hide
*/
public interface OnDrmConfigHelper {
/**
@@ -3196,9 +3191,10 @@
* Register a callback to be invoked for configuration of the DRM object before
* the session is created.
* The callback will be invoked synchronously during the execution
- * of {@link #prepareDrm(DataSourceDesc, UUID)}.
+ * of {@link #prepareDrm}.
*
* @param listener the callback that will be run
+ * @hide
*/
// This is a synchronous call.
public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
@@ -3210,6 +3206,7 @@
/**
* Interface definition for callbacks to be invoked when the player has the corresponding
* DRM events.
+ * @hide
*/
public static class DrmEventCallback {
/**
@@ -3243,6 +3240,7 @@
*
* @param eventCallback the callback that will be run
* @param executor the executor through which the callback should be invoked
+ * @hide
*/
// This is a synchronous call.
public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
@@ -3263,6 +3261,7 @@
* Unregisters the {@link DrmEventCallback}.
*
* @param eventCallback the callback to be unregistered
+ * @hide
*/
// This is a synchronous call.
public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
@@ -3280,31 +3279,37 @@
* <p>
*
* DRM preparation has succeeded.
+ * @hide
*/
public static final int PREPARE_DRM_STATUS_SUCCESS = 0;
/**
* The device required DRM provisioning but couldn't reach the provisioning server.
+ * @hide
*/
public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1;
/**
* The device required DRM provisioning but the provisioning server denied the request.
+ * @hide
*/
public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2;
/**
* The DRM preparation has failed .
+ * @hide
*/
public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
/**
* The crypto scheme UUID is not supported by the device.
+ * @hide
*/
public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4;
/**
* The hardware resources are not available, due to being in use.
+ * @hide
*/
public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5;
@@ -3345,6 +3350,7 @@
* @param dsd The DRM protected data source
*
* @throws IllegalStateException if called before being prepared
+ * @hide
*/
public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
final SourceInfo sourceInfo = getSourceInfo(dsd);
@@ -3400,6 +3406,7 @@
* {@link DrmEventCallback#onDrmInfo}.
*
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
+ * @hide
*/
// This is an asynchronous call.
public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
@@ -3493,6 +3500,7 @@
* @param dsd The DRM protected data source
*
* @throws NoDrmSchemeException if there is no active DRM session to release
+ * @hide
*/
// This is a synchronous call.
public void releaseDrm(@NonNull DataSourceDesc dsd)
@@ -3503,7 +3511,7 @@
}
}
- private native void native_releaseDrm();
+ private native void native_releaseDrm(long mSrcId);
/**
* A key request/response exchange occurs between the app and a license server
@@ -3543,6 +3551,7 @@
* This may be {@code null} if no additional parameters are to be sent.
*
* @throws NoDrmSchemeException if there is no active DRM session
+ * @hide
*/
public MediaDrm.KeyRequest getDrmKeyRequest(
@NonNull DataSourceDesc dsd,
@@ -3583,6 +3592,7 @@
* @throws NoDrmSchemeException if there is no active DRM session
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
+ * @hide
*/
// This is a synchronous call.
public byte[] provideDrmKeyResponse(
@@ -3608,6 +3618,7 @@
* @param keySetId identifies the saved key set to restore
*
* @throws NoDrmSchemeException if there is no active DRM session
+ * @hide
*/
// This is a synchronous call.
public void restoreDrmKeys(
@@ -3635,6 +3646,7 @@
* {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
*
* @throws NoDrmSchemeException if there is no active DRM session
+ * @hide
*/
public String getDrmPropertyString(
@NonNull DataSourceDesc dsd,
@@ -3661,6 +3673,7 @@
* {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
*
* @throws NoDrmSchemeException if there is no active DRM session
+ * @hide
*/
// This is a synchronous call.
public void setDrmPropertyString(
@@ -3678,6 +3691,7 @@
/**
* Encapsulates the DRM properties of the source.
+ * @hide
*/
public static final class DrmInfo {
private Map<UUID, byte[]> mMapPssh;
@@ -3704,36 +3718,37 @@
mSupportedSchemes = supportedSchemes;
}
- private DrmInfo(PlayerMessage msg) {
- Log.v(TAG, "DrmInfo(" + msg + ")");
+ private static DrmInfo create(PlayerMessage msg) {
+ Log.v(TAG, "DrmInfo.create(" + msg + ")");
Iterator<Value> in = msg.getValuesList().iterator();
byte[] pssh = in.next().getBytesValue().toByteArray();
- Log.v(TAG, "DrmInfo() PSSH: " + arrToHex(pssh));
- mMapPssh = parsePSSH(pssh, pssh.length);
- Log.v(TAG, "DrmInfo() PSSH: " + mMapPssh);
+ Log.v(TAG, "DrmInfo.create() PSSH: " + DrmInfo.arrToHex(pssh));
+ Map<UUID, byte[]> mapPssh = DrmInfo.parsePSSH(pssh, pssh.length);
+ Log.v(TAG, "DrmInfo.create() PSSH: " + mapPssh);
int supportedDRMsCount = in.next().getInt32Value();
- mSupportedSchemes = new UUID[supportedDRMsCount];
+ UUID[] supportedSchemes = new UUID[supportedDRMsCount];
for (int i = 0; i < supportedDRMsCount; i++) {
byte[] uuid = new byte[16];
in.next().getBytesValue().copyTo(uuid, 0);
- mSupportedSchemes[i] = bytesToUUID(uuid);
+ supportedSchemes[i] = DrmInfo.bytesToUUID(uuid);
- Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + mSupportedSchemes[i]);
+ Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + supportedSchemes[i]);
}
- Log.v(TAG, "DrmInfo() psshsize: " + pssh.length
+ Log.v(TAG, "DrmInfo.create() psshsize: " + pssh.length
+ " supportedDRMsCount: " + supportedDRMsCount);
+ return new DrmInfo(mapPssh, supportedSchemes);
}
private DrmInfo makeCopy() {
return new DrmInfo(this.mMapPssh, this.mSupportedSchemes);
}
- private String arrToHex(byte[] bytes) {
+ private static String arrToHex(byte[] bytes) {
String out = "0x";
for (int i = 0; i < bytes.length; i++) {
out += String.format("%02x", bytes[i]);
@@ -3742,7 +3757,7 @@
return out;
}
- private UUID bytesToUUID(byte[] uuid) {
+ private static UUID bytesToUUID(byte[] uuid) {
long msb = 0, lsb = 0;
for (int i = 0; i < 8; i++) {
msb |= (((long) uuid[i] & 0xff) << (8 * (7 - i)));
@@ -3752,7 +3767,7 @@
return new UUID(msb, lsb);
}
- private Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) {
+ private static Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) {
Map<UUID, byte[]> result = new HashMap<UUID, byte[]>();
final int uuidSize = 16;
@@ -3816,6 +3831,7 @@
* Thrown when a DRM method is called before preparing a DRM scheme through
* {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}.
* Extends MediaDrm.MediaDrmException
+ * @hide
*/
public static final class NoDrmSchemeException extends MediaDrmException {
public NoDrmSchemeException(String detailMessage) {
@@ -3823,7 +3839,8 @@
}
}
- private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
+ private native void native_prepareDrm(
+ long srcId, @NonNull byte[] uuid, @NonNull byte[] drmSessionId);
// Instantiated from the native side
@SuppressWarnings("unused")
@@ -3943,7 +3960,12 @@
textBounds = new Rect(left, top, right, bottom);
}
}
+ return null;
+ /* TimedText c-tor usage is temporarily commented out.
+ * TODO(b/117527789): use SUBTITLE path for MEDIA_MIMETYPE_TEXT_3GPP track
+ * and remove TimedText path from MediaPlayer2.
return new TimedText(textChars, textBounds);
+ */
}
}
@@ -4066,6 +4088,7 @@
static final int PROVISION_TIMEOUT_MS = 60000;
final DataSourceDesc mDSD;
+ final long mSrcId;
//--- guarded by |this| start
MediaDrm mDrmObj;
@@ -4077,8 +4100,9 @@
Future<?> mProvisionResult;
//--- guarded by |this| end
- DrmHandle(DataSourceDesc dsd) {
+ DrmHandle(DataSourceDesc dsd, long srcId) {
mDSD = dsd;
+ mSrcId = srcId;
}
void prepare(UUID uuid) throws UnsupportedSchemeException,
@@ -4188,7 +4212,8 @@
// Sending it down to native/mediaserver to create the crypto object
// This call could simply fail due to bad player state, e.g., after play().
- MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
+ final MediaPlayer2 mp2 = MediaPlayer2.this;
+ mp2.native_prepareDrm(mSrcId, getByteArrayFromUUID(uuid), mDrmSessionId);
Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
} catch (Exception e) { //ResourceBusyException, NotProvisionedException
@@ -4369,7 +4394,7 @@
// exception if we're in a non-stopped/prepared state.
// for cleaning native/mediaserver crypto object
- native_releaseDrm();
+ native_releaseDrm(mSrcId);
// for cleaning client-side MediaDrm object; only called if above has succeeded
cleanDrmObj();
@@ -4575,7 +4600,7 @@
SourceInfo(DataSourceDesc dsd) {
this.mDSD = dsd;
- mDrmHandle = new DrmHandle(dsd);
+ mDrmHandle = new DrmHandle(dsd, mId);
}
void close() {
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index d4bfd61..8ced021 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -22,17 +22,17 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.hardware.Camera;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.Surface;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -41,8 +41,6 @@
import java.util.ArrayList;
import java.util.List;
-import com.android.internal.annotations.GuardedBy;
-
/**
* Used to record audio and video. The recording control is based on a
* simple state machine (see below).
@@ -450,6 +448,9 @@
/** VP8/VORBIS data in a WEBM container */
public static final int WEBM = 9;
+
+ /** Opus data in a Ogg container */
+ public static final int OGG = 11;
};
/**
@@ -474,6 +475,8 @@
public static final int AAC_ELD = 5;
/** Ogg Vorbis audio codec */
public static final int VORBIS = 6;
+ /** Opus audio codec */
+ public static final int OPUS = 7;
}
/**
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 3a64f43..90cfc53 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -156,7 +156,8 @@
private static final String NOTIFICATIONS_DIR = "/notifications/";
private static final String ALARMS_DIR = "/alarms/";
private static final String MUSIC_DIR = "/music/";
- private static final String PODCAST_DIR = "/podcasts/";
+ private static final String PODCASTS_DIR = "/podcasts/";
+ private static final String AUDIOBOOKS_DIR = "/audiobooks/";
public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
@@ -654,7 +655,7 @@
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
if (noMedia) {
- result = endFile(entry, false, false, false, false, false);
+ result = endFile(entry, false, false, false, false, false, false);
} else {
boolean isaudio = MediaFile.isAudioMimeType(mMimeType);
boolean isvideo = MediaFile.isVideoMimeType(mMimeType);
@@ -679,11 +680,13 @@
boolean notifications = mScanSuccess &&
(lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
- boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCAST_DIR) > 0);
+ boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCASTS_DIR) > 0);
+ boolean audiobooks = mScanSuccess && (lowpath.indexOf(AUDIOBOOKS_DIR) > 0);
boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
- (!ringtones && !notifications && !alarms && !podcasts));
+ (!ringtones && !notifications && !alarms && !podcasts && !audiobooks));
- result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
+ result = endFile(entry, ringtones, notifications, alarms, podcasts,
+ audiobooks, music);
}
}
} catch (RemoteException e) {
@@ -957,7 +960,7 @@
@UnsupportedAppUsage
private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
- boolean alarms, boolean music, boolean podcasts)
+ boolean alarms, boolean podcasts, boolean audiobooks, boolean music)
throws RemoteException {
// update database
@@ -1003,6 +1006,7 @@
values.put(Audio.Media.IS_ALARM, alarms);
values.put(Audio.Media.IS_MUSIC, music);
values.put(Audio.Media.IS_PODCAST, podcasts);
+ values.put(Audio.Media.IS_AUDIOBOOK, audiobooks);
} else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) {
ExifInterface exif = null;
try {
@@ -1011,12 +1015,6 @@
// exif is null
}
if (exif != null) {
- float[] latlng = new float[2];
- if (exif.getLatLong(latlng)) {
- values.put(Images.Media.LATITUDE, latlng[0]);
- values.put(Images.Media.LONGITUDE, latlng[1]);
- }
-
long time = exif.getGpsDateTime();
if (time != -1) {
values.put(Images.Media.DATE_TAKEN, time);
diff --git a/media/java/android/media/MediaTimestamp.java b/media/java/android/media/MediaTimestamp.java
index e079a8e..03e454c 100644
--- a/media/java/android/media/MediaTimestamp.java
+++ b/media/java/android/media/MediaTimestamp.java
@@ -16,6 +16,9 @@
package android.media;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
/**
* An immutable object that represents the linear correlation between the media time
* and the system time. It contains the media clock rate, together with the media timestamp
@@ -117,4 +120,71 @@
+ " clockRate=" + clockRate
+ "}";
}
+
+ /**
+ * Builder class for {@link MediaTimestamp} objects.
+ * <p> Here is an example where <code>Builder</code> is used to define the
+ * {@link MediaTimestamp}:
+ *
+ * <pre class="prettyprint">
+ * MediaTimestamp mts = new MediaTimestamp.Builder()
+ * .setMediaTimestamp(mediaTime, systemTime, rate)
+ * .build();
+ * </pre>
+ * @hide
+ */
+ @SystemApi
+ public static class Builder {
+ long mMediaTimeUs;
+ long mNanoTime;
+ float mClockRate = 1.0f;
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given {@link MediaTimestamp} instance
+ * @param mts the {@link MediaTimestamp} object whose data will be reused
+ * in the new Builder.
+ */
+ public Builder(@NonNull MediaTimestamp mts) {
+ if (mts == null) {
+ throw new IllegalArgumentException("null MediaTimestamp is not allowed");
+ }
+ mMediaTimeUs = mts.mediaTimeUs;
+ mNanoTime = mts.nanoTime;
+ mClockRate = mts.clockRate;
+ }
+
+ /**
+ * Combines all of the fields that have been set and return a new
+ * {@link MediaTimestamp} object.
+ *
+ * @return a new {@link MediaTimestamp} object
+ */
+ public @NonNull MediaTimestamp build() {
+ return new MediaTimestamp(mMediaTimeUs, mNanoTime, mClockRate);
+ }
+
+ /**
+ * Sets the info of media timestamp.
+ *
+ * @param mediaTimeUs the media time of the anchor in microseconds
+ * @param nanoTime the {@link java.lang.System#nanoTime system time} corresponding to
+ * the media time in nanoseconds.
+ * @param clockRate the rate of the media clock in relation to the system time.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setMediaTimestamp(
+ long mediaTimeUs, long nanoTime, float clockRate) {
+ mMediaTimeUs = mediaTimeUs;
+ mNanoTime = nanoTime;
+ mClockRate = clockRate;
+
+ return this;
+ }
+ }
}
diff --git a/media/java/android/media/SubtitleData.java b/media/java/android/media/SubtitleData.java
index ba37b9b..852babe 100644
--- a/media/java/android/media/SubtitleData.java
+++ b/media/java/android/media/SubtitleData.java
@@ -17,8 +17,11 @@
package android.media;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
+import java.util.Arrays;
+
/**
* Class encapsulating subtitle data, as received through the
* {@link MediaPlayer.OnSubtitleDataListener} interface.
@@ -80,11 +83,11 @@
}
/** @hide */
- public SubtitleData(int trackIndex, long startTimeUs, long durationUs, byte[] data) {
+ public SubtitleData(int trackIndex, long startTimeUs, long durationUs, @NonNull byte[] data) {
mTrackIndex = trackIndex;
mStartTimeUs = startTimeUs;
mDurationUs = durationUs;
- mData = data;
+ mData = (data != null ? data : new byte[0]);
}
/**
@@ -138,4 +141,80 @@
return true;
}
+
+ /**
+ * Builder class for {@link SubtitleData} objects.
+ * <p> Here is an example where <code>Builder</code> is used to define the
+ * {@link SubtitleData}:
+ *
+ * <pre class="prettyprint">
+ * SubtitleData sd = new SubtitleData.Builder()
+ * .setSubtitleData(trackIndex, startTime, duration, data)
+ * .build();
+ * </pre>
+ * @hide
+ */
+ @SystemApi
+ public static class Builder {
+ private int mTrackIndex;
+ private long mStartTimeUs;
+ private long mDurationUs;
+ private byte[] mData = new byte[0];
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given {@link SubtitleData} instance
+ * @param sd the {@link SubtitleData} object whose data will be reused
+ * in the new Builder. It should not be null. The data array is copied.
+ */
+ public Builder(@NonNull SubtitleData sd) {
+ if (sd == null) {
+ throw new IllegalArgumentException("null SubtitleData is not allowed");
+ }
+ mTrackIndex = sd.mTrackIndex;
+ mStartTimeUs = sd.mStartTimeUs;
+ mDurationUs = sd.mDurationUs;
+ if (sd.mData != null) {
+ mData = Arrays.copyOf(sd.mData, sd.mData.length);
+ }
+ }
+
+ /**
+ * Combines all of the fields that have been set and return a new
+ * {@link SubtitleData} object. <code>IllegalStateException</code> will be
+ * thrown if there is conflict between fields.
+ *
+ * @return a new {@link SubtitleData} object
+ */
+ public @NonNull SubtitleData build() {
+ return new SubtitleData(mTrackIndex, mStartTimeUs, mDurationUs, mData);
+ }
+
+ /**
+ * Sets the info of subtitle data.
+ *
+ * @param trackIndex the ParcelFileDescriptor for the file to play
+ * @param startTimeUs the start time in microsecond for the subtile data
+ * @param durationUs the duration in microsecond for the subtile data
+ * @param data the data array for the subtile data. It should not be null.
+ * No data copying is made.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setSubtitleData(
+ int trackIndex, long startTimeUs, long durationUs, @NonNull byte[] data) {
+ if (data == null) {
+ throw new IllegalArgumentException("null data is not allowed");
+ }
+ mTrackIndex = trackIndex;
+ mStartTimeUs = startTimeUs;
+ mDurationUs = durationUs;
+ mData = data;
+ return this;
+ }
+ }
}
diff --git a/media/java/android/media/TimedMetaData.java b/media/java/android/media/TimedMetaData.java
index 97e6bfa..2a89888 100644
--- a/media/java/android/media/TimedMetaData.java
+++ b/media/java/android/media/TimedMetaData.java
@@ -16,8 +16,12 @@
package android.media;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
+import java.util.Arrays;
+
/**
* Class that embodies one timed metadata access unit, including
*
@@ -50,7 +54,10 @@
/**
* @hide
*/
- public TimedMetaData(long timestampUs, byte[] metaData) {
+ public TimedMetaData(long timestampUs, @NonNull byte[] metaData) {
+ if (metaData == null) {
+ throw new IllegalArgumentException("null metaData is not allowed");
+ }
mTimestampUs = timestampUs;
mMetaData = metaData;
}
@@ -83,4 +90,71 @@
return true;
}
+
+ /**
+ * Builder class for {@link TimedMetaData} objects.
+ * <p> Here is an example where <code>Builder</code> is used to define the
+ * {@link TimedMetaData}:
+ *
+ * <pre class="prettyprint">
+ * TimedMetaData tmd = new TimedMetaData.Builder()
+ * .setTimedMetaData(timestamp, metaData)
+ * .build();
+ * </pre>
+ * @hide
+ */
+ @SystemApi
+ public static class Builder {
+ private long mTimestampUs;
+ private byte[] mMetaData = new byte[0];
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given {@link TimedMetaData} instance
+ * @param tmd the {@link TimedMetaData} object whose data will be reused
+ * in the new Builder. It should not be null. The metadata array is copied.
+ */
+ public Builder(@NonNull TimedMetaData tmd) {
+ if (tmd == null) {
+ throw new IllegalArgumentException("null TimedMetaData is not allowed");
+ }
+ mTimestampUs = tmd.mTimestampUs;
+ if (tmd.mMetaData != null) {
+ mMetaData = Arrays.copyOf(tmd.mMetaData, tmd.mMetaData.length);
+ }
+ }
+
+ /**
+ * Combines all of the fields that have been set and return a new
+ * {@link TimedMetaData} object. <code>IllegalStateException</code> will be
+ * thrown if there is conflict between fields.
+ *
+ * @return a new {@link TimedMetaData} object
+ */
+ public @NonNull TimedMetaData build() {
+ return new TimedMetaData(mTimestampUs, mMetaData);
+ }
+
+ /**
+ * Sets the info of timed metadata.
+ *
+ * @param timestamp the timestamp in microsecond for the timed metadata
+ * @param metaData the metadata array for the timed metadata. No data copying is made.
+ * It should not be null.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setTimedMetaData(long timestamp, @NonNull byte[] metaData) {
+ if (metaData == null) {
+ throw new IllegalArgumentException("null metaData is not allowed");
+ }
+ mTimestampUs = timestamp;
+ mMetaData = metaData;
+ return this;
+ }
+ }
}
diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java
index 6a83dab..4eb9e8d 100644
--- a/media/java/android/media/UriDataSourceDesc.java
+++ b/media/java/android/media/UriDataSourceDesc.java
@@ -30,13 +30,10 @@
import java.util.Map;
/**
- * @hide
* Structure of data source descriptor for sources using URI.
*
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
- * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
- * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
- * to set data source for playback.
+ * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
+ * {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.
*
diff --git a/media/java/android/media/VideoSize.java b/media/java/android/media/VideoSize.java
index 7e5cb1f..19631e0 100644
--- a/media/java/android/media/VideoSize.java
+++ b/media/java/android/media/VideoSize.java
@@ -18,8 +18,6 @@
/**
* Immutable class for describing width and height dimensions.
- *
- * @hide
*/
public final class VideoSize {
/**
@@ -28,7 +26,7 @@
* @param width The width of the video size
* @param height The height of the video size
*/
- public VideoSize(int width, int height) {
+ VideoSize(int width, int height) {
mWidth = width;
mHeight = height;
}
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index a7bdf4f..89a509f 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -18,11 +18,12 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
-import android.util.Log;
-import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
/**
* The Visualizer class enables application to retrieve part of the currently playing audio for
@@ -455,7 +456,7 @@
* <li> Rfk, Ifk are respectively the real and imaginary parts of the kth frequency
* component</li>
* <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
- * (k*Fs)/(n/2) </li>
+ * k * Fs / n </li>
* </ul>
* <table border="0" cellspacing="0" cellpadding="0">
* <tr><td>Index </p></td>
@@ -476,9 +477,23 @@
* <td>Rf2 </p></td>
* <td>If2 </p></td>
* <td>... </p></td>
- * <td>Rf(n-1)/2 </p></td>
- * <td>If(n-1)/2 </p></td></tr>
+ * <td>Rf(n/2-1) </p></td>
+ * <td>If(n/2-1) </p></td></tr>
* </table>
+ * <p>In order to obtain magnitude and phase values the following code can
+ * be used:
+ * <pre class="prettyprint">
+ * int n = fft.size();
+ * float[] magnitudes = new float[n / 2 + 1];
+ * float[] phases = new float[n / 2 + 1];
+ * magnitudes[0] = (float)Math.abs(fft[0]); // DC
+ * magnitudes[n / 2] = (float)Math.abs(fft[1]); // Nyquist
+ * phases[0] = phases[n / 2] = 0;
+ * for (int k = 1; k < n / 2; k++) {
+ * int i = k * 2;
+ * magnitudes[k] = (float)Math.hypot(fft[i], fft[i + 1]);
+ * phases[k] = (float)Math.atan2(fft[i + 1], fft[i]);
+ * }</pre>
* @param fft array of bytes where the FFT should be returned
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
@@ -561,25 +576,11 @@
* <p>Data in the fft buffer is valid only within the scope of the callback.
* Applications which need access to the fft data after returning from the callback
* should make a copy of the data instead of holding a reference.
+ * <p>For the explanation of the fft data array layout, and the example
+ * code for processing it, please see the documentation for {@link #getFft(byte[])} method.
*
- * <p>In order to obtain magnitude and phase values the following formulas can
- * be used:
- * <pre class="prettyprint">
- * for (int i = 0; i < fft.size(); i += 2) {
- * float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
- * float phase = (float)Math.atan2(fft[i + 1], fft[i]);
- * }</pre>
* @param visualizer Visualizer object on which the listener is registered.
* @param fft array of bytes containing the frequency representation.
- * The fft array only contains the first half of the actual
- * FFT spectrum (frequencies up to Nyquist frequency), exploiting
- * the symmetry of the spectrum. For each frequencies bin <code>i</code>:
- * <ul>
- * <li>the element at index <code>2*i</code> in the array contains
- * the real part of a complex number,</li>
- * <li>the element at index <code>2*i+1</code> contains the imaginary
- * part of the complex number.</li>
- * </ul>
* @param samplingRate sampling rate of the visualized audio.
*/
void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 8bc1d35..7fb3aa6 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -81,14 +81,12 @@
* An audio mix behavior where the output of the mix is sent to the original destination of
* the audio signal, i.e. an output device for an output mix, or a recording for an input mix.
*/
- @SystemApi
public static final int ROUTE_FLAG_RENDER = 0x1;
/**
* An audio mix behavior where the output of the mix is rerouted back to the framework and
* is accessible for injection or capture through the {@link AudioTrack} and {@link AudioRecord}
* APIs.
*/
- @SystemApi
public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1;
private static final int ROUTE_FLAG_SUPPORTED = ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK;
@@ -113,31 +111,23 @@
// MIX_STATE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h
/**
- * @hide
* State of a mix before its policy is enabled.
*/
- @SystemApi
public static final int MIX_STATE_DISABLED = -1;
/**
- * @hide
* State of a mix when there is no audio to mix.
*/
- @SystemApi
public static final int MIX_STATE_IDLE = 0;
/**
- * @hide
* State of a mix that is actively mixing audio.
*/
- @SystemApi
public static final int MIX_STATE_MIXING = 1;
/**
- * @hide
* The current mixing state.
* @return one of {@link #MIX_STATE_DISABLED}, {@link #MIX_STATE_IDLE},
* {@link #MIX_STATE_MIXING}.
*/
- @SystemApi
public int getMixState() {
return mMixState;
}
@@ -201,9 +191,7 @@
/**
* Builder class for {@link AudioMix} objects
- *
*/
- @SystemApi
public static class Builder {
private AudioMixingRule mRule = null;
private AudioFormat mFormat = null;
@@ -224,7 +212,6 @@
* @param rule a non-null {@link AudioMixingRule} instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder(AudioMixingRule rule)
throws IllegalArgumentException {
if (rule == null) {
@@ -284,7 +271,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setFormat(AudioFormat format)
throws IllegalArgumentException {
if (format == null) {
@@ -302,7 +288,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setRouteFlags(@RouteFlags int routeFlags)
throws IllegalArgumentException {
if (routeFlags == 0) {
@@ -329,7 +314,6 @@
* @return the same Builder instance
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException {
if (device == null) {
throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument");
@@ -347,7 +331,6 @@
* @return a new {@link AudioMix} object
* @throws IllegalArgumentException if no {@link AudioMixingRule} has been set.
*/
- @SystemApi
public AudioMix build() throws IllegalArgumentException {
if (mRule == null) {
throw new IllegalArgumentException("Illegal null AudioMixingRule");
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index fbee62a..6c48cdb 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -54,7 +54,6 @@
* {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
* {@link AudioAttributes}.
*/
- @SystemApi
public static final int RULE_MATCH_ATTRIBUTE_USAGE = 0x1;
/**
* A rule requiring the capture preset information of the {@link AudioAttributes} to match.
@@ -62,14 +61,12 @@
* {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
* {@link AudioAttributes}.
*/
- @SystemApi
public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 0x1 << 1;
/**
* A rule requiring the UID of the audio stream to match that specified.
* This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object
* parameter is an instance of {@link java.lang.Integer}.
*/
- @SystemApi
public static final int RULE_MATCH_UID = 0x1 << 2;
private final static int RULE_EXCLUSION_MASK = 0x8000;
@@ -239,7 +236,6 @@
/**
* Builder class for {@link AudioMixingRule} objects
*/
- @SystemApi
public static class Builder {
private ArrayList<AudioMixMatchCriterion> mCriteria;
private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
@@ -247,7 +243,6 @@
/**
* Constructs a new Builder with no rules.
*/
- @SystemApi
public Builder() {
mCriteria = new ArrayList<AudioMixMatchCriterion>();
}
@@ -262,7 +257,6 @@
* @throws IllegalArgumentException
* @see #excludeRule(AudioAttributes, int)
*/
- @SystemApi
public Builder addRule(AudioAttributes attrToMatch, int rule)
throws IllegalArgumentException {
if (!isValidAttributesSystemApiRule(rule)) {
@@ -291,7 +285,6 @@
* @throws IllegalArgumentException
* @see #addRule(AudioAttributes, int)
*/
- @SystemApi
public Builder excludeRule(AudioAttributes attrToMatch, int rule)
throws IllegalArgumentException {
if (!isValidAttributesSystemApiRule(rule)) {
@@ -313,7 +306,6 @@
* @throws IllegalArgumentException
* @see #excludeMixRule(int, Object)
*/
- @SystemApi
public Builder addMixRule(int rule, Object property) throws IllegalArgumentException {
if (!isValidSystemApiRule(rule)) {
throw new IllegalArgumentException("Illegal rule value " + rule);
@@ -343,7 +335,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder excludeMixRule(int rule, Object property) throws IllegalArgumentException {
if (!isValidSystemApiRule(rule)) {
throw new IllegalArgumentException("Illegal rule value " + rule);
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 11107e2..6103f557 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -58,12 +58,10 @@
/**
* The status of an audio policy that is valid but cannot be used because it is not registered.
*/
- @SystemApi
public static final int POLICY_STATUS_UNREGISTERED = 1;
/**
* The status of an audio policy that is valid, successfully registered and thus active.
*/
- @SystemApi
public static final int POLICY_STATUS_REGISTERED = 2;
private int mStatus;
@@ -75,7 +73,6 @@
* The behavior of a policy with regards to audio focus where it relies on the application
* to do the ducking, the is the legacy and default behavior.
*/
- @SystemApi
public static final int FOCUS_POLICY_DUCKING_IN_APP = 0;
public static final int FOCUS_POLICY_DUCKING_DEFAULT = FOCUS_POLICY_DUCKING_IN_APP;
/**
@@ -85,7 +82,6 @@
* <br>Can only be used after having set a listener with
* {@link AudioPolicy#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
*/
- @SystemApi
public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1;
private AudioPolicyFocusListener mFocusListener;
@@ -133,7 +129,6 @@
* Builder class for {@link AudioPolicy} objects.
* By default the policy to be created doesn't govern audio focus decisions.
*/
- @SystemApi
public static class Builder {
private ArrayList<AudioMix> mMixes;
private Context mContext;
@@ -147,7 +142,6 @@
* Constructs a new Builder with no audio mixes.
* @param context the context for the policy
*/
- @SystemApi
public Builder(Context context) {
mMixes = new ArrayList<AudioMix>();
mContext = context;
@@ -159,7 +153,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder addMix(@NonNull AudioMix mix) throws IllegalArgumentException {
if (mix == null) {
throw new IllegalArgumentException("Illegal null AudioMix argument");
@@ -174,7 +167,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setLooper(@NonNull Looper looper) throws IllegalArgumentException {
if (looper == null) {
throw new IllegalArgumentException("Illegal null Looper argument");
@@ -187,7 +179,6 @@
* Sets the audio focus listener for the policy.
* @param l a {@link AudioPolicy.AudioPolicyFocusListener}
*/
- @SystemApi
public void setAudioPolicyFocusListener(AudioPolicyFocusListener l) {
mFocusListener = l;
}
@@ -201,7 +192,6 @@
* @param enforce true if the policy will govern audio focus decisions.
* @return the same Builder instance.
*/
- @SystemApi
public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) {
mIsFocusPolicy = isFocusPolicy;
return this;
@@ -211,12 +201,10 @@
* Sets the audio policy status listener.
* @param l a {@link AudioPolicy.AudioPolicyStatusListener}
*/
- @SystemApi
public void setAudioPolicyStatusListener(AudioPolicyStatusListener l) {
mStatusListener = l;
}
- @SystemApi
/**
* Sets the callback to receive all volume key-related events.
* The callback will only be called if the device is configured to handle volume events
@@ -240,7 +228,6 @@
* {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured
* as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}.
*/
- @SystemApi
public AudioPolicy build() {
if (mStatusListener != null) {
// the AudioPolicy status listener includes updates on each mix activity state
@@ -258,7 +245,6 @@
}
/**
- * @hide
* Update the current configuration of the set of audio mixes by adding new ones, while
* keeping the policy registered.
* This method can only be called on a registered policy.
@@ -266,7 +252,6 @@
* @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
* otherwise.
*/
- @SystemApi
public int attachMixes(@NonNull List<AudioMix> mixes) {
if (mixes == null) {
throw new IllegalArgumentException("Illegal null list of AudioMix");
@@ -299,7 +284,6 @@
}
/**
- * @hide
* Update the current configuration of the set of audio mixes by removing some, while
* keeping the policy registered.
* This method can only be called on a registered policy.
@@ -307,7 +291,6 @@
* @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
* otherwise.
*/
- @SystemApi
public int detachMixes(@NonNull List<AudioMix> mixes) {
if (mixes == null) {
throw new IllegalArgumentException("Illegal null list of AudioMix");
@@ -405,7 +388,6 @@
* Returns the current behavior for audio focus-related ducking.
* @return {@link #FOCUS_POLICY_DUCKING_IN_APP} or {@link #FOCUS_POLICY_DUCKING_IN_POLICY}
*/
- @SystemApi
public int getFocusDuckingBehavior() {
return mConfig.mDuckingPolicy;
}
@@ -422,7 +404,6 @@
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
- @SystemApi
public int setFocusDuckingBehavior(int behavior)
throws IllegalArgumentException, IllegalStateException {
if ((behavior != FOCUS_POLICY_DUCKING_IN_APP)
@@ -466,7 +447,6 @@
* with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
* @throws IllegalArgumentException
*/
- @SystemApi
public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException {
if (!policyReadyToUse()) {
Log.e(TAG, "Cannot create AudioRecord sink for AudioMix");
@@ -506,7 +486,6 @@
* with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
* @throws IllegalArgumentException
*/
- @SystemApi
public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException {
if (!policyReadyToUse()) {
Log.e(TAG, "Cannot create AudioTrack source for AudioMix");
@@ -528,18 +507,15 @@
return at;
}
- @SystemApi
public int getStatus() {
return mStatus;
}
- @SystemApi
public static abstract class AudioPolicyStatusListener {
public void onStatusChange() {}
public void onMixStateUpdate(AudioMix mix) {}
}
- @SystemApi
public static abstract class AudioPolicyFocusListener {
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
@@ -563,7 +539,6 @@
public void onAudioFocusAbandon(AudioFocusInfo afi) {}
}
- @SystemApi
/**
* Callback class to receive volume change-related events.
* See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8215779..6a06dd0 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -620,12 +620,24 @@
private final int mUid;
private final IBinder mCallerBinder;
+ /**
+ * Create a new remote user information.
+ *
+ * @param packageName The package name of the remote user
+ * @param pid The pid of the remote user
+ * @param uid The uid of the remote user
+ */
public RemoteUserInfo(@NonNull String packageName, int pid, int uid) {
this(packageName, pid, uid, null);
}
/**
- * @hide
+ * Create a new remote user information.
+ *
+ * @param packageName The package name of the remote user
+ * @param pid The pid of the remote user
+ * @param uid The uid of the remote user
+ * @param callerBinder The binder of the remote user. Can be {@code null}.
*/
public RemoteUserInfo(String packageName, int pid, int uid, IBinder callerBinder) {
mPackageName = packageName;
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index ec2d9ba..e8f188c 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1332,7 +1332,6 @@
*
* @return the list of content ratings blocked by the user.
*/
- @SystemApi
public List<TvContentRating> getBlockedRatings() {
try {
List<TvContentRating> ratings = new ArrayList<>();
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 5037209..2578608 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -752,6 +752,13 @@
}
}
+void JMediaCodec::selectAudioPresentation(const int32_t presentationId, const int32_t programId) {
+ sp<AMessage> msg = new AMessage;
+ msg->setInt32("audio-presentation-presentation-id", presentationId);
+ msg->setInt32("audio-presentation-program-id", programId);
+ (void)mCodec->setParameters(msg);
+}
+
static jthrowable createCodecException(
JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) {
ScopedLocalRef<jclass> clazz(
@@ -1874,6 +1881,18 @@
codec->setVideoScalingMode(mode);
}
+static void android_media_MediaCodec_setAudioPresentation(
+ JNIEnv *env, jobject thiz, jint presentationId, jint programId) {
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ codec->selectAudioPresentation((int32_t)presentationId, (int32_t)programId);
+}
+
static void android_media_MediaCodec_native_init(JNIEnv *env) {
ScopedLocalRef<jclass> clazz(
env, env->FindClass("android/media/MediaCodec"));
@@ -2183,6 +2202,9 @@
{ "setVideoScalingMode", "(I)V",
(void *)android_media_MediaCodec_setVideoScalingMode },
+ { "native_setAudioPresentation", "(II)V",
+ (void *)android_media_MediaCodec_setAudioPresentation },
+
{ "native_init", "()V", (void *)android_media_MediaCodec_native_init },
{ "native_setup", "(Ljava/lang/String;ZZ)V",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 985f55a..0a53f1a 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -128,6 +128,8 @@
void setVideoScalingMode(int mode);
+ void selectAudioPresentation(const int32_t presentationId, const int32_t programId);
+
protected:
virtual ~JMediaCodec();
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 29238d3..f3442f4 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -500,17 +500,17 @@
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
int64_t sampleTimeUs;
status_t err = extractor->getSampleTime(&sampleTimeUs);
if (err == ERROR_END_OF_STREAM) {
- return -1ll;
+ return -1LL;
} else if (err != OK) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return -1ll;
+ return -1LL;
}
return (jlong) sampleTimeUs;
@@ -522,17 +522,17 @@
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
size_t sampleSize;
status_t err = extractor->getSampleSize(&sampleSize);
if (err == ERROR_END_OF_STREAM) {
- return -1ll;
+ return -1LL;
} else if (err != OK) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return -1ll;
+ return -1LL;
}
return (jlong) sampleSize;
@@ -858,13 +858,13 @@
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
int64_t cachedDurationUs;
bool eos;
if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
- return -1ll;
+ return -1LL;
}
return (jlong) cachedDurationUs;
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 4567492..8b6009e 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1192,7 +1192,7 @@
}
static void android_media_MediaPlayer2_prepareDrm(JNIEnv *env, jobject thiz,
- jbyteArray uuidObj, jbyteArray drmSessionIdObj)
+ jlong srcId, jbyteArray uuidObj, jbyteArray drmSessionIdObj)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
@@ -1225,7 +1225,7 @@
return;
}
- status_t err = mp->prepareDrm(uuid.array(), drmSessionId);
+ status_t err = mp->prepareDrm(srcId, uuid.array(), drmSessionId);
if (err != OK) {
if (err == INVALID_OPERATION) {
jniThrowException(
@@ -1243,7 +1243,7 @@
}
}
-static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz)
+static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz, jlong srcId)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -1251,7 +1251,7 @@
return;
}
- status_t err = mp->releaseDrm();
+ status_t err = mp->releaseDrm(srcId);
if (err != OK) {
if (err == INVALID_OPERATION) {
jniThrowException(
@@ -1425,8 +1425,8 @@
{"native_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
{"native_attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
// Modular DRM
- { "native_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
- { "native_releaseDrm", "()V", (void *)android_media_MediaPlayer2_releaseDrm },
+ { "native_prepareDrm", "(J[B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
+ { "native_releaseDrm", "(J)V", (void *)android_media_MediaPlayer2_releaseDrm },
// AudioRouting
{"native_setPreferredDevice", "(Landroid/media/AudioDeviceInfo;)Z", (void *)android_media_MediaPlayer2_setPreferredDevice},
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 693bd8b..747d4c01 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "android_media_AudioEffect.h"
#include <stdio.h>
@@ -29,6 +28,10 @@
#include <nativehelper/ScopedUtfChars.h>
+#include "android_media_AudioEffect.h"
+#include "android_media_AudioEffectDescriptor.h"
+#include "android_media_AudioErrors.h"
+
using namespace android;
#define AUDIOEFFECT_SUCCESS 0
@@ -49,8 +52,6 @@
jmethodID midPostNativeEvent; // event post callback method
jfieldID fidNativeAudioEffect; // stores in Java the native AudioEffect object
jfieldID fidJniData; // stores in Java additional resources used by the native AudioEffect
- jclass clazzDesc; // AudioEffect.Descriptor class
- jmethodID midDescCstor; // AudioEffect.Descriptor class constructor
};
static fields_t fields;
@@ -226,7 +227,6 @@
ALOGV("android_media_AudioEffect_native_init");
fields.clazzEffect = NULL;
- fields.clazzDesc = NULL;
// Get the AudioEffect class
jclass clazz = env->FindClass(kClassPathName);
@@ -263,23 +263,6 @@
ALOGE("Can't find AudioEffect.%s", "mJniData");
return;
}
-
- clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
- if (clazz == NULL) {
- ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
- return;
- }
- fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
-
- fields.midDescCstor
- = env->GetMethodID(
- fields.clazzDesc,
- "<init>",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
- if (fields.midDescCstor == NULL) {
- ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
- return;
- }
}
@@ -297,12 +280,6 @@
const char *uuidStr = NULL;
effect_descriptor_t desc;
jobject jdesc;
- char str[EFFECT_STRING_LEN_MAX];
- jstring jdescType;
- jstring jdescUuid;
- jstring jdescConnect;
- jstring jdescName;
- jstring jdescImplementor;
ScopedUtfChars opPackageNameStr(env, opPackageName);
@@ -394,41 +371,12 @@
// get the effect descriptor
desc = lpAudioEffect->descriptor();
- AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
- jdescType = env->NewStringUTF(str);
-
- AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
- jdescUuid = env->NewStringUTF(str);
-
- if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- jdescConnect = env->NewStringUTF("Auxiliary");
- } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
- jdescConnect = env->NewStringUTF("Pre Processing");
- } else {
- jdescConnect = env->NewStringUTF("Insert");
- }
-
- jdescName = env->NewStringUTF(desc.name);
- jdescImplementor = env->NewStringUTF(desc.implementor);
-
- jdesc = env->NewObject(fields.clazzDesc,
- fields.midDescCstor,
- jdescType,
- jdescUuid,
- jdescConnect,
- jdescName,
- jdescImplementor);
- env->DeleteLocalRef(jdescType);
- env->DeleteLocalRef(jdescUuid);
- env->DeleteLocalRef(jdescConnect);
- env->DeleteLocalRef(jdescName);
- env->DeleteLocalRef(jdescImplementor);
- if (jdesc == NULL) {
- ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
+ if (convertAudioEffectDescriptorFromNative(env, &jdesc, &desc) != AUDIO_JAVA_SUCCESS) {
goto setup_failure;
}
env->SetObjectArrayElement(javadesc, 0, jdesc);
+ env->DeleteLocalRef(jdesc);
setAudioEffect(env, thiz, lpAudioEffect);
@@ -729,23 +677,16 @@
android_media_AudioEffect_native_queryEffects(JNIEnv *env, jclass clazz __unused)
{
effect_descriptor_t desc;
- char str[EFFECT_STRING_LEN_MAX];
uint32_t totalEffectsCount = 0;
uint32_t returnedEffectsCount = 0;
uint32_t i = 0;
- jstring jdescType;
- jstring jdescUuid;
- jstring jdescConnect;
- jstring jdescName;
- jstring jdescImplementor;
- jobject jdesc;
jobjectArray ret;
if (AudioEffect::queryNumberEffects(&totalEffectsCount) != NO_ERROR) {
return NULL;
}
- jobjectArray temp = env->NewObjectArray(totalEffectsCount, fields.clazzDesc, NULL);
+ jobjectArray temp = env->NewObjectArray(totalEffectsCount, audioEffectDescriptorClass(), NULL);
if (temp == NULL) {
return temp;
}
@@ -757,49 +698,18 @@
goto queryEffects_failure;
}
- if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- jdescConnect = env->NewStringUTF("Auxiliary");
- } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT) {
- jdescConnect = env->NewStringUTF("Insert");
- } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
- jdescConnect = env->NewStringUTF("Pre Processing");
- } else {
+ jobject jdesc;
+ if (convertAudioEffectDescriptorFromNative(env, &jdesc, &desc) != AUDIO_JAVA_SUCCESS) {
continue;
}
-
- AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
- jdescType = env->NewStringUTF(str);
-
- AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
- jdescUuid = env->NewStringUTF(str);
-
- jdescName = env->NewStringUTF(desc.name);
- jdescImplementor = env->NewStringUTF(desc.implementor);
-
- jdesc = env->NewObject(fields.clazzDesc,
- fields.midDescCstor,
- jdescType,
- jdescUuid,
- jdescConnect,
- jdescName,
- jdescImplementor);
- env->DeleteLocalRef(jdescType);
- env->DeleteLocalRef(jdescUuid);
- env->DeleteLocalRef(jdescConnect);
- env->DeleteLocalRef(jdescName);
- env->DeleteLocalRef(jdescImplementor);
- if (jdesc == NULL) {
- ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
- goto queryEffects_failure;
- }
-
env->SetObjectArrayElement(temp, returnedEffectsCount++, jdesc);
- }
+ env->DeleteLocalRef(jdesc);
+ }
if (returnedEffectsCount == 0) {
goto queryEffects_failure;
}
- ret = env->NewObjectArray(returnedEffectsCount, fields.clazzDesc, NULL);
+ ret = env->NewObjectArray(returnedEffectsCount, audioEffectDescriptorClass(), NULL);
if (ret == NULL) {
goto queryEffects_failure;
}
@@ -835,51 +745,11 @@
}
ALOGV("queryDefaultPreProcessing() got %d effects", numEffects);
- jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
- if (ret == NULL) {
- return ret;
- }
+ std::vector<effect_descriptor_t> descVector(descriptors.get(), descriptors.get() + numEffects);
- char str[EFFECT_STRING_LEN_MAX];
- jstring jdescType;
- jstring jdescUuid;
- jstring jdescConnect;
- jstring jdescName;
- jstring jdescImplementor;
- jobject jdesc;
-
- for (uint32_t i = 0; i < numEffects; i++) {
-
- AudioEffect::guidToString(&descriptors[i].type, str, EFFECT_STRING_LEN_MAX);
- jdescType = env->NewStringUTF(str);
- AudioEffect::guidToString(&descriptors[i].uuid, str, EFFECT_STRING_LEN_MAX);
- jdescUuid = env->NewStringUTF(str);
- jdescConnect = env->NewStringUTF("Pre Processing");
- jdescName = env->NewStringUTF(descriptors[i].name);
- jdescImplementor = env->NewStringUTF(descriptors[i].implementor);
-
- jdesc = env->NewObject(fields.clazzDesc,
- fields.midDescCstor,
- jdescType,
- jdescUuid,
- jdescConnect,
- jdescName,
- jdescImplementor);
- env->DeleteLocalRef(jdescType);
- env->DeleteLocalRef(jdescUuid);
- env->DeleteLocalRef(jdescConnect);
- env->DeleteLocalRef(jdescName);
- env->DeleteLocalRef(jdescImplementor);
- if (jdesc == NULL) {
- ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
- env->DeleteLocalRef(ret);
- return NULL;
- }
-
- env->SetObjectArrayElement(ret, i, jdesc);
- }
-
- return ret;
+ jobjectArray ret;
+ convertAudioEffectDescriptorVectorFromNative(env, &ret, descVector);
+ return ret;
}
// ----------------------------------------------------------------------------
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index b7d7b03..45de36e 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -440,6 +440,7 @@
if (lpVisualizer == 0) {
return;
}
+ lpVisualizer->release();
}
// delete the JNI data
VisualizerJniStorage* lpJniStorage =
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index ed2afdd..5f51320 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -513,7 +513,8 @@
static status_t decode(int fd, int64_t offset, int64_t length,
uint32_t *rate, int *numChannels, audio_format_t *audioFormat,
- sp<MemoryHeapBase> heap, size_t *memsize) {
+ audio_channel_mask_t *channelMask, sp<MemoryHeapBase> heap,
+ size_t *memsize) {
ALOGV("fd %d, offset %" PRId64 ", size %" PRId64, fd, offset, length);
AMediaExtractor *ex = AMediaExtractor_new();
@@ -650,6 +651,10 @@
(void)AMediaFormat_delete(format);
return UNKNOWN_ERROR;
}
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_MASK,
+ (int32_t*) channelMask)) {
+ *channelMask = AUDIO_CHANNEL_NONE;
+ }
(void)AMediaFormat_delete(format);
*memsize = written;
return OK;
@@ -665,12 +670,13 @@
uint32_t sampleRate;
int numChannels;
audio_format_t format;
+ audio_channel_mask_t channelMask;
status_t status;
mHeap = new MemoryHeapBase(kDefaultHeapSize);
ALOGV("Start decode");
status = decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
- mHeap, &mSize);
+ &channelMask, mHeap, &mSize);
ALOGV("close(%d)", mFd);
::close(mFd);
mFd = -1;
@@ -697,6 +703,7 @@
mSampleRate = sampleRate;
mNumChannels = numChannels;
mFormat = format;
+ mChannelMask = channelMask;
mState = READY;
return NO_ERROR;
@@ -781,7 +788,11 @@
// wrong audio audio buffer size (mAudioBufferSize)
unsigned long toggle = mToggle ^ 1;
void *userData = (void *)((unsigned long)this | toggle);
- audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(numChannels);
+ audio_channel_mask_t sampleChannelMask = sample->channelMask();
+ // When sample contains a not none channel mask, use it as is.
+ // Otherwise, use channel count to calculate channel mask.
+ audio_channel_mask_t channelMask = sampleChannelMask != AUDIO_CHANNEL_NONE
+ ? sampleChannelMask : audio_channel_out_mask_from_count(numChannels);
// do not create a new audio track if current track is compatible with sample parameters
#ifdef USE_SHARED_MEM_BUFFER
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 5c48a90..9d74103 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -58,6 +58,7 @@
int numChannels() { return mNumChannels; }
int sampleRate() { return mSampleRate; }
audio_format_t format() { return mFormat; }
+ audio_channel_mask_t channelMask() { return mChannelMask; }
size_t size() { return mSize; }
int state() { return mState; }
uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
@@ -68,18 +69,19 @@
private:
void init();
- size_t mSize;
- volatile int32_t mRefCount;
- uint16_t mSampleID;
- uint16_t mSampleRate;
- uint8_t mState;
- uint8_t mNumChannels;
- audio_format_t mFormat;
- int mFd;
- int64_t mOffset;
- int64_t mLength;
- sp<IMemory> mData;
- sp<MemoryHeapBase> mHeap;
+ size_t mSize;
+ volatile int32_t mRefCount;
+ uint16_t mSampleID;
+ uint16_t mSampleRate;
+ uint8_t mState;
+ uint8_t mNumChannels;
+ audio_format_t mFormat;
+ audio_channel_mask_t mChannelMask;
+ int mFd;
+ int64_t mOffset;
+ int64_t mLength;
+ sp<IMemory> mData;
+ sp<MemoryHeapBase> mHeap;
};
// stores pending events for stolen channels
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 207508e..537aed4 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -229,9 +229,16 @@
ATrace_beginSection; # introduced=23
ATrace_endSection; # introduced=23
ATrace_isEnabled; # introduced=23
+ ATrace_beginAsyncSection; # introduced=29
+ ATrace_endAsyncSection; # introduced=29
+ ATrace_setCounter; # introduced=29
android_getaddrinfofornetwork; # introduced=23
android_setprocnetwork; # introduced=23
android_setsocknetwork; # introduced=23
+ android_res_cancel; # introduced=29
+ android_res_nquery; # introduced=29
+ android_res_nresult; # introduced=29
+ android_res_nsend; # introduced=29
local:
*;
};
diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt
index 9b5a5a1..be3531d 100644
--- a/native/android/libandroid_net.map.txt
+++ b/native/android/libandroid_net.map.txt
@@ -1,10 +1,15 @@
-# These functions have been part of the NDK since API 24.
# They are also all available to vendor code.
LIBANDROID_NET {
global:
+ # These functions have been part of the NDK since API 24.
+ android_getaddrinfofornetwork; # vndk
android_setsocknetwork; # vndk
android_setprocnetwork; # vndk
- android_getaddrinfofornetwork; # vndk
+ # These functions have been part of the NDK since API 29.
+ android_res_cancel; # vndk
+ android_res_nquery; # vndk
+ android_res_nresult; # vndk
+ android_res_nsend; # vndk
local:
*;
};
diff --git a/native/android/net.c b/native/android/net.c
index 60296a7..e32b787 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -83,3 +83,31 @@
return android_getaddrinfofornet(node, service, hints, netid, 0, res);
}
+
+int android_res_nquery(net_handle_t network,
+ const char *dname, int ns_class, int ns_type) {
+ unsigned netid;
+ if (!getnetidfromhandle(network, &netid)) {
+ return -ENONET;
+ }
+
+ return resNetworkQuery(netid, dname, ns_class, ns_type);
+}
+
+int android_res_nresult(int fd, int *rcode, unsigned char *answer, int anslen) {
+ return resNetworkResult(fd, rcode, answer, anslen);
+}
+
+int android_res_nsend(net_handle_t network,
+ const unsigned char *msg, int msglen) {
+ unsigned netid;
+ if (!getnetidfromhandle(network, &netid)) {
+ return -ENONET;
+ }
+
+ return resNetworkSend(netid, msg, msglen);
+}
+
+void android_res_cancel(int nsend_fd) {
+ resNetworkCancel(nsend_fd);
+}
diff --git a/native/android/trace.cpp b/native/android/trace.cpp
index db52220..9ff5b8a 100644
--- a/native/android/trace.cpp
+++ b/native/android/trace.cpp
@@ -28,3 +28,15 @@
void ATrace_endSection() {
atrace_end(ATRACE_TAG_APP);
}
+
+void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) {
+ atrace_async_begin(ATRACE_TAG_APP, sectionName, cookie);
+}
+
+void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) {
+ atrace_async_end(ATRACE_TAG_APP, sectionName, cookie);
+}
+
+void ATrace_setCounter(const char* counterName, int64_t counterValue) {
+ atrace_int64(ATRACE_TAG_APP, counterName, counterValue);
+}
\ No newline at end of file
diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp
index d8c5ac9..96c9c1c 100644
--- a/native/webview/plat_support/Android.bp
+++ b/native/webview/plat_support/Android.bp
@@ -23,6 +23,8 @@
srcs: [
"draw_gl_functor.cpp",
+ "draw_vk_functor.cpp",
+ "functor_utils.cpp",
"jni_entry_point.cpp",
"graphics_utils.cpp",
"graphic_buffer_impl.cpp",
@@ -36,6 +38,7 @@
"liblog",
"libui",
"libutils",
+ "libvulkan",
],
// To remove warnings from skia header files
diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
index e3e52b1..be36b67 100644
--- a/native/webview/plat_support/draw_gl_functor.cpp
+++ b/native/webview/plat_support/draw_gl_functor.cpp
@@ -21,15 +21,13 @@
#include "draw_gl.h"
-#include <errno.h>
#include <jni.h>
#include <private/hwui/DrawGlInfo.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <sys/time.h>
#include <utils/Functor.h>
#include <utils/Log.h>
+#include "functor_utils.h"
+
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#define COMPILE_ASSERT(expr, err) \
__unused static const char (err)[(expr) ? 1 : -1] = "";
@@ -98,27 +96,6 @@
intptr_t view_context_;
};
-// Raise the file handle soft limit to the hard limit since gralloc buffers
-// uses file handles.
-void RaiseFileNumberLimit() {
- static bool have_raised_limit = false;
- if (have_raised_limit)
- return;
-
- have_raised_limit = true;
- struct rlimit limit_struct;
- limit_struct.rlim_cur = 0;
- limit_struct.rlim_max = 0;
- if (getrlimit(RLIMIT_NOFILE, &limit_struct) == 0) {
- limit_struct.rlim_cur = limit_struct.rlim_max;
- if (setrlimit(RLIMIT_NOFILE, &limit_struct) != 0) {
- ALOGE("setrlimit failed: %s", strerror(errno));
- }
- } else {
- ALOGE("getrlimit failed: %s", strerror(errno));
- }
-}
-
jlong CreateGLFunctor(JNIEnv*, jclass, jlong view_context) {
RaiseFileNumberLimit();
return reinterpret_cast<jlong>(new DrawGLFunctor(view_context));
diff --git a/native/webview/plat_support/draw_vk.h b/native/webview/plat_support/draw_vk.h
new file mode 100644
index 0000000..6b7d8d0
--- /dev/null
+++ b/native/webview/plat_support/draw_vk.h
@@ -0,0 +1,125 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+//******************************************************************************
+// This is a copy of the coresponding android_webview/public/browser header.
+// Any changes to the interface should be made there.
+//
+// The purpose of having the copy is twofold:
+// - it removes the need to have Chromium sources present in the tree in order
+// to build the plat_support library,
+// - it captures API that the corresponding Android release supports.
+//******************************************************************************
+
+#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
+#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
+
+#include <vulkan/vulkan.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static const int kAwDrawVKInfoVersion = 1;
+
+// Holds the information required to trigger initialization of the Vulkan
+// functor.
+struct InitParams {
+ // All params are input
+ VkInstance instance;
+ VkPhysicalDevice physical_device;
+ VkDevice device;
+ VkQueue queue;
+ uint32_t graphics_queue_index;
+ uint32_t instance_version;
+ const char* const* enabled_extension_names;
+ // Only one of device_features and device_features_2 should be non-null.
+ // If both are null then no features are enabled.
+ VkPhysicalDeviceFeatures* device_features;
+ VkPhysicalDeviceFeatures2* device_features_2;
+};
+
+// Holds the information required to trigger an Vulkan composite operation.
+struct CompositeParams {
+ // Input: current width/height of destination surface.
+ int width;
+ int height;
+
+ // Input: is the render target a FBO
+ bool is_layer;
+
+ // Input: current transform matrix
+ float transform[16];
+
+ // Input WebView should do its main compositing draws into this. It cannot do
+ // anything that would require stopping the render pass.
+ VkCommandBuffer secondary_command_buffer;
+
+ // Input: The main color attachment index where secondary_command_buffer will
+ // eventually be submitted.
+ uint32_t color_attachment_index;
+
+ // Input: A render pass which will be compatible to the one which the
+ // secondary_command_buffer will be submitted into.
+ VkRenderPass compatible_render_pass;
+
+ // Input: Format of the destination surface.
+ VkFormat format;
+
+ // Input: Color space transfer params
+ float G;
+ float A;
+ float B;
+ float C;
+ float D;
+ float E;
+ float F;
+
+ // Input: Color space transformation from linear RGB to D50-adapted XYZ
+ float matrix[9];
+
+ // Input: current clip rect
+ int clip_left;
+ int clip_top;
+ int clip_right;
+ int clip_bottom;
+};
+
+// Holds the information for the post-submission callback of main composite
+// draw.
+struct PostCompositeParams {
+ // Input: Fence for the composite command buffer to signal it has finished its
+ // work on the GPU.
+ int fd;
+};
+
+// Holds the information required to trigger an Vulkan operation.
+struct AwDrawVKInfo {
+ int version; // The AwDrawVKInfo this struct was built with.
+
+ // Input: tells the draw function what action to perform.
+ enum Mode {
+ kModeInit = 0,
+ kModeReInit = 1,
+ kModePreComposite = 2,
+ kModeComposite = 3,
+ kModePostComposite = 4,
+ kModeSync = 5,
+ } mode;
+
+ // Input: The parameters for the functor being called
+ union ParamUnion {
+ struct InitParams init_params;
+ struct CompositeParams composite_params;
+ struct PostCompositeParams post_composite_params;
+ } info;
+};
+
+typedef void(AwDrawVKFunction)(long view_context, AwDrawVKInfo* draw_info);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
diff --git a/native/webview/plat_support/draw_vk_functor.cpp b/native/webview/plat_support/draw_vk_functor.cpp
new file mode 100644
index 0000000..1ba559d
--- /dev/null
+++ b/native/webview/plat_support/draw_vk_functor.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Provides a webviewchromium glue layer adapter from the internal Android
+// Vulkan Functor data types into the types the chromium stack expects, and
+// back.
+
+#define LOG_TAG "webviewchromium_plat_support"
+
+#include "draw_vk.h"
+
+#include <jni.h>
+#include <private/hwui/DrawVkInfo.h>
+#include <utils/Functor.h>
+#include <utils/Log.h>
+
+#include "functor_utils.h"
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+namespace android {
+namespace {
+
+AwDrawVKFunction* g_aw_drawvk_function = NULL;
+
+class DrawVKFunctor : public Functor {
+ public:
+ explicit DrawVKFunctor(jlong view_context) : view_context_(view_context) {}
+ ~DrawVKFunctor() override {}
+
+ // Functor
+ status_t operator ()(int what, void* data) override {
+ using uirenderer::DrawVkInfo;
+ if (!g_aw_drawvk_function) {
+ ALOGE("Cannot draw: no DrawVK Function installed");
+ return DrawVkInfo::kStatusDone;
+ }
+
+ AwDrawVKInfo aw_info;
+ aw_info.version = kAwDrawVKInfoVersion;
+ switch (what) {
+ case DrawVkInfo::kModeComposite: {
+ aw_info.mode = AwDrawVKInfo::kModeComposite;
+ DrawVkInfo* vk_info = reinterpret_cast<DrawVkInfo*>(data);
+
+ // Map across the input values.
+ CompositeParams& params = aw_info.info.composite_params;
+ params.width = vk_info->width;
+ params.height = vk_info->height;
+ params.is_layer = vk_info->isLayer;
+ for (size_t i = 0; i < 16; i++) {
+ params.transform[i] = vk_info->transform[i];
+ }
+ params.secondary_command_buffer = vk_info->secondaryCommandBuffer;
+ params.color_attachment_index = vk_info->colorAttachmentIndex;
+ params.compatible_render_pass = vk_info->compatibleRenderPass;
+ params.format = vk_info->format;
+ params.G = vk_info->G;
+ params.A = vk_info->A;
+ params.B = vk_info->B;
+ params.C = vk_info->C;
+ params.D = vk_info->D;
+ params.E = vk_info->E;
+ params.F = vk_info->F;
+ for (size_t i = 0; i < 9; i++) {
+ params.matrix[i] = vk_info->matrix[i];
+ }
+ params.clip_left = vk_info->clipLeft;
+ params.clip_top = vk_info->clipTop;
+ params.clip_right = vk_info->clipRight;
+ params.clip_bottom = vk_info->clipBottom;
+
+ break;
+ }
+ case DrawVkInfo::kModePostComposite:
+ break;
+ case DrawVkInfo::kModeSync:
+ aw_info.mode = AwDrawVKInfo::kModeSync;
+ break;
+ default:
+ ALOGE("Unexpected DrawVKInfo type %d", what);
+ return DrawVkInfo::kStatusDone;
+ }
+
+ // Invoke the DrawVK method.
+ g_aw_drawvk_function(view_context_, &aw_info);
+
+ return DrawVkInfo::kStatusDone;
+ }
+
+ private:
+ intptr_t view_context_;
+};
+
+jlong CreateVKFunctor(JNIEnv*, jclass, jlong view_context) {
+ RaiseFileNumberLimit();
+ return reinterpret_cast<jlong>(new DrawVKFunctor(view_context));
+}
+
+void DestroyVKFunctor(JNIEnv*, jclass, jlong functor) {
+ delete reinterpret_cast<DrawVKFunctor*>(functor);
+}
+
+void SetChromiumAwDrawVKFunction(JNIEnv*, jclass, jlong draw_function) {
+ g_aw_drawvk_function = reinterpret_cast<AwDrawVKFunction*>(draw_function);
+}
+
+const char kClassName[] = "com/android/webview/chromium/DrawVKFunctor";
+const JNINativeMethod kJniMethods[] = {
+ { "nativeCreateVKFunctor", "(J)J",
+ reinterpret_cast<void*>(CreateVKFunctor) },
+ { "nativeDestroyVKFunctor", "(J)V",
+ reinterpret_cast<void*>(DestroyVKFunctor) },
+ { "nativeSetChromiumAwDrawVKFunction", "(J)V",
+ reinterpret_cast<void*>(SetChromiumAwDrawVKFunction) },
+};
+
+} // namespace
+
+void RegisterDrawVKFunctor(JNIEnv* env) {
+ jclass clazz = env->FindClass(kClassName);
+ LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName);
+
+ int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res);
+}
+
+} // namespace android
diff --git a/native/webview/plat_support/functor_utils.cpp b/native/webview/plat_support/functor_utils.cpp
new file mode 100644
index 0000000..235762d
--- /dev/null
+++ b/native/webview/plat_support/functor_utils.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "functor_utils.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <utils/Log.h>
+
+namespace android {
+
+void RaiseFileNumberLimit() {
+ static bool have_raised_limit = false;
+ if (have_raised_limit)
+ return;
+
+ have_raised_limit = true;
+ struct rlimit limit_struct;
+ limit_struct.rlim_cur = 0;
+ limit_struct.rlim_max = 0;
+ if (getrlimit(RLIMIT_NOFILE, &limit_struct) == 0) {
+ limit_struct.rlim_cur = limit_struct.rlim_max;
+ if (setrlimit(RLIMIT_NOFILE, &limit_struct) != 0) {
+ ALOGE("setrlimit failed: %s", strerror(errno));
+ }
+ } else {
+ ALOGE("getrlimit failed: %s", strerror(errno));
+ }
+}
+
+} // namespace android
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/native/webview/plat_support/functor_utils.h
similarity index 76%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to native/webview/plat_support/functor_utils.h
index c66a6cb..76c0bb6 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/native/webview/plat_support/functor_utils.h
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.view.intelligence;
+#pragma once
-parcelable ContentCaptureEvent;
+namespace android {
+
+// Raise the file handle soft limit to the hard limit since gralloc buffers
+// uses file handles.
+void RaiseFileNumberLimit();
+
+} // namespace android
diff --git a/packages/CaptivePortalLogin/res/values-af/strings.xml b/packages/CaptivePortalLogin/res/values-af/strings.xml
index cf4dc82..37acf45 100644
--- a/packages/CaptivePortalLogin/res/values-af/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-af/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Die netwerk waarby jy probeer aansluit, het sekuriteitkwessies."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Gaan in elk geval deur blaaier voort"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Bladsy-inligting"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sekuriteitswaarskuwing"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Bekyk sertifikaat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Hierdie sertifikaat is nie van \'n betroubare owerheid nie."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Die naam van die werf kom nie ooreen met die naam op die sertifikaat nie."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Hierdie sertifikaat het verval."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Hierdie sertifikaat is nog nie geldig nie."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Hierdie sertifikaat het \'n ongeldige datum."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Hierdie sertifikaat is ongeldig."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende sertifikaatfout."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Hierdie sertifikaat is nie van \'n betroubare owerheid nie."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Die naam van die werf stem nie ooreen met die naam op die sertifikaat nie."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Hierdie sertifikaat het verval."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Hierdie sertifikaat is nog nie geldig nie."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Hierdie sertifikaat het \'n ongeldige datum."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Hierdie sertifikaat is ongeldig."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Onbekende sertifikaatfout."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sekuriteitswaarskuwing"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Bekyk sertifikaat"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Bladsy-inligting"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-am/strings.xml b/packages/CaptivePortalLogin/res/values-am/strings.xml
index cdcb5a5..4e35e19 100644
--- a/packages/CaptivePortalLogin/res/values-am/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-am/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ለመቀላቀል እየሞከሩ ያሉት አውታረ መረብ የደህንነት ችግሮች አሉበት።"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string>
- <string name="ok" msgid="1509280796718850364">"እሺ"</string>
- <string name="page_info" msgid="4048529256302257195">"የገፅ መረጃ"</string>
- <string name="page_info_address" msgid="2222306609532903254">"አድራሻ:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"የደህንነት ቅንብሮች"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ምስክሮች ይመልከቱ"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"ይህ ምስክር ከታማኝ ቦታ አይደለም።"</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"የጣቢያው ስም ከምስክር ወረቀቱ ስም ጋር አይዛመድም።"</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"ይህ ምስክር ጊዜው አልፏል"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ይህ ምስክር ገና ትክክል አይደለም።"</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ይህ ምስክር ትክክለኛ ቀን አለው።"</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"ይህ ምስክር ትክክል ያልሆነ ነው።"</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"ያልታወቀ የምስክር ስህተት።"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ይህ የእውቅና ማረጋገጫ ከታማኝ ቦታ አይደለም።"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"የጣቢያው ስም ከእውቅና ማረጋገጫው ስም ጋር አይዛመድም።"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ይህ የእውቅና ማረጋገጫ ጊዜው አልፏል።"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ይህ የእውቅና ማረጋገጫ ገና የሚሰራ አይደለም።"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ይህ የእውቅና ማረጋገጫ ልክ ያልሆነ ቀን ነው ያለው።"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ይህ የእውቅና ማረጋገጫ ልክ ያልሆነ ነው።"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"ያልታወቀ የእውቅና ማረጋገጫ ስህተት።"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"የደህንነት ቅንብሮች"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"የእውቅና ማረጋገጫን ይመልከቱ"</string>
+ <string name="ok" msgid="2817931639040794018">"እሺ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"አድራሻ፦"</string>
+ <string name="page_info" msgid="4416941086705172545">"የገጽ መረጃ"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ar/strings.xml b/packages/CaptivePortalLogin/res/values-ar/strings.xml
index 7773eeb..9f3aefa 100644
--- a/packages/CaptivePortalLogin/res/values-ar/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ar/strings.xml
@@ -11,16 +11,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"الشبكة التي تحاول الانضمام إليها بها مشاكل أمنية."</string>
<string name="ssl_error_example" msgid="647898534624078900">"على سبيل المثال، قد لا تنتمي صفحة تسجيل الدخول إلى المنظمة المعروضة."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"المتابعة على أي حال عبر المتصفح"</string>
- <string name="ok" msgid="1509280796718850364">"موافق"</string>
- <string name="page_info" msgid="4048529256302257195">"معلومات الصفحة"</string>
- <string name="page_info_address" msgid="2222306609532903254">"العنوان:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"تحذير أمان"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"عرض الشهادة"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"هذه الشهادة ليست من جهة موثوق بها."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"لا يتطابق اسم الموقع مع الاسم على الشهادة."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"انتهت صلاحية هذه الشهادة."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"هذه الشهادة ليست صالحة بعد."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تشتمل هذه الشهادة على تاريخ غير صالح."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"هذه الشهادة غير صالحة."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"حدث خطأ غير معروف بالشهادة."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"هذه الشهادة ليست من جهة موثوق بها."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"لا يتطابق اسم الموقع مع الاسم على الشهادة."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"انتهت صلاحية هذه الشهادة."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"هذه الشهادة ليست صالحة بعد."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"تشتمل هذه الشهادة على تاريخ غير صالح."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"هذه الشهادة غير صالحة."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"حدث خطأ غير معروف بالشهادة."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"تحذير أمني"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"عرض الشهادة"</string>
+ <string name="ok" msgid="2817931639040794018">"حسنًا"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"العنوان:"</string>
+ <string name="page_info" msgid="4416941086705172545">"معلومات الصفحة"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-as/strings.xml b/packages/CaptivePortalLogin/res/values-as/strings.xml
index 94c3147..696483c 100644
--- a/packages/CaptivePortalLogin/res/values-as/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-as/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"আপুনি সংযোগ কৰিবলৈ চেষ্টা কৰি থকা নেটৱৰ্কটোত সুৰক্ষাজনিত সমস্যা আছে।"</string>
<string name="ssl_error_example" msgid="647898534624078900">"উদাহৰণস্বৰূপে, আপোনাক দেখুওৱা লগ ইনৰ পৃষ্ঠাটো প্ৰতিষ্ঠানটোৰ নিজা নহ\'বও পাৰে।"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"তথাপি ব্ৰাউজাৰৰ জৰিয়তে অব্যাহত ৰাখক"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"এই প্ৰমাণপত্ৰখন কোনো বিশ্বাসী কর্তৃপক্ষৰ নহয়।"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ৱেবচাইটটোৰ নাম আৰু প্ৰমাণপত্ৰখনত থকা নামটোৰ মিল নাই।"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"এই প্ৰমাণপত্ৰখনৰ ম্যাদ উকলি গৈছে।"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"এই প্ৰমাণপত্ৰখন এই পর্যন্ত মান্য হোৱা নাই।"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"এই প্ৰমাণপত্ৰখনত থকা তাৰিখটো মান্য নহয়।"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"এই প্ৰমাণপত্ৰখন মান্য নহয়।"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"প্ৰমাণপত্ৰত অজ্ঞাত আসোঁৱাহ।"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"সুৰক্ষা সম্পর্কীয় সতৰ্কতা"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"প্ৰমাণপত্ৰ চাওক"</string>
+ <string name="ok" msgid="2817931639040794018">"ঠিক আছে"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ঠিকনা:"</string>
+ <string name="page_info" msgid="4416941086705172545">"পৃষ্ঠাৰ তথ্য"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-az/strings.xml b/packages/CaptivePortalLogin/res/values-az/strings.xml
index 44b406d..f002544 100644
--- a/packages/CaptivePortalLogin/res/values-az/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-az/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Qoşulmaq istədiyiniz şəbəkənin təhlükəsizlik problemləri var."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Məsələn, giriş səhifəsi göstərilən təşkilata aid olmaya bilər."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Hər bir halda brazuer ilə davam edin"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Bu sertifikat etibarlı orqan tərəfindən verilməyib."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Saytın adı sertifikatdakı ada uyğun deyil."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Bu sertifikatın müddəti bitib."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Bu sertifikat hələ etibarlı deyil."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Bu sertifikatın tarixi yanlışdır."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Bu sertifikat etibarsızdır."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Bilinməyən sertifikat xətası."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Təhlükəsizlik xəbərdarlığı"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Sertifikata baxın"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Ünvan:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Səhifə məlumatı"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml b/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
index f2a6e07..401548b 100644
--- a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj pokušavate da se pridružite ima bezbednosnih problema."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi preko pregledača"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ovaj sertifikat ne potiče iz pouzdanog izvora."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Naziv sajta se ne podudara sa nazivom na sertifikatu."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Ovaj sertifikat je istekao."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ovaj sertifikat još uvek nije važeći."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Datum ovog sertifikata je nevažeći."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Ovaj sertifikat je nevažeći."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Nepoznata greška sertifikata."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Bezbednosno upozorenje"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Prikaži sertifikat"</string>
+ <string name="ok" msgid="2817931639040794018">"Potvrdi"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informacije o stranici"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-be/strings.xml b/packages/CaptivePortalLogin/res/values-be/strings.xml
index 09ed1de..5f2bd90 100644
--- a/packages/CaptivePortalLogin/res/values-be/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-be/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"У сеткі, да якой вы спрабуеце далучыцца, ёсць праблемы з бяспекай."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Напрыклад, старонка ўваходу можа не належаць указанай арганізацыі."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Усё роўна працягнуць праз браўзер"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Гэты сертыфікат не выдадзены давераным цэнтрам."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Назва сайта не адпавядае назве ў сертыфікаце."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Тэрмін дзеяння сертыфіката мінуў."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Гэты сертыфікат яшчэ не дзейнічае."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Гэты сертыфікат мае несапраўдную дату."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Гэты сертыфікат несапраўдны."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Невядомая памылка сертыфіката."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Папярэджанне сістэмы бяспекі"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Прагледзець сертыфікат"</string>
+ <string name="ok" msgid="2817931639040794018">"ОК"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Адрас:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Інфармацыя пра старонку"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-bg/strings.xml b/packages/CaptivePortalLogin/res/values-bg/strings.xml
index 4dd8aa0..c5354e8 100644
--- a/packages/CaptivePortalLogin/res/values-bg/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bg/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата, към която опитвате да се присъедините, има проблеми със сигурността."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Например страницата за вход може да не принадлежи на показаната организация."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Продължаване през браузър въпреки това"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Данни за страницата"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Предупреждение относно защитата"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Преглед на сертификата"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертификатът не е от надежден орган."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Името на сайта не съответства на името в сертификата."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Сертификатът е изтекъл."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификатът още не е валиден."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Този сертификат е с невалидна дата."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Този сертификат е невалиден."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестна грешка в сертификата."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Този сертификат не е от надежден орган."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Името на сайта не съответства на името в сертификата."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Този сертификат е изтекъл."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Този сертификат още не е валиден."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Този сертификат е с невалидна дата."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Този сертификат е невалиден."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Неизвестна грешка в сертификата."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Предупреждение относно сигурността"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Преглед на сертификата"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Адрес:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Данни за страницата"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-bn/strings.xml b/packages/CaptivePortalLogin/res/values-bn/strings.xml
index fb703cf..3fbe2f2 100644
--- a/packages/CaptivePortalLogin/res/values-bn/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bn/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"আপনি যে নেটওয়ার্কে যোগ দেওয়ার চেষ্টা করছেন তাতে নিরাপত্তার সমস্যা আছে।"</string>
<string name="ssl_error_example" msgid="647898534624078900">"উদাহরণস্বরূপ, লগ-ইন পৃষ্ঠাটি প্রদর্শিত প্রতিষ্ঠানের অন্তর্গত নাও হতে পারে৷"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"যাই হোক না কেন ব্রাউজারের মাধ্যমে অবিরত রাখুন"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"এই সার্টিফিকেটটি একটি বিশ্বস্ত কর্তৃপক্ষের তরফ থেকে নয়।"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"সাইটের নামটি সার্টিফিকেটে লেখা নামের সাথে মিলছে না।"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"এই সার্টিফিকেটের মেয়াদ শেষ হয়ে গেছে।"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"সার্টিফিকেটটি এখনও সঠিক নয়।"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"এই সার্টিফিকেটের তারিখটি সঠিক নয়।"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"সার্টিফিকেটটি সঠিক নয়।"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"সার্টিফিকেটের সমস্যাটি অজানা।"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"নিরাপত্তার সতর্কতা"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"সার্টিফিকেট দেখুন"</string>
+ <string name="ok" msgid="2817931639040794018">"ঠিক আছে"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ঠিকানা:"</string>
+ <string name="page_info" msgid="4416941086705172545">"পৃষ্ঠার তথ্য"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-bs/strings.xml b/packages/CaptivePortalLogin/res/values-bs/strings.xml
index 10be0e5..60c153a 100644
--- a/packages/CaptivePortalLogin/res/values-bs/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bs/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj pokušavate pristupiti ima sigurnosnih problema."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Naprimjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi preko preglednika"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ova potvrda ne potiče iz pouzdanog izvora."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Naziv web lokacije se ne podudara s nazivom na potvrdi."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Ova potvrda je istekla."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ova potvrda još uvijek nije važeća."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Ova potvrda sadrži nevažeći datum."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Ova potvrda je nevažeća."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Nepoznata greška potvrde."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sigurnosno upozorenje"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Prikaži potvrdu"</string>
+ <string name="ok" msgid="2817931639040794018">"UREDU"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informacije o stranici"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ca/strings.xml b/packages/CaptivePortalLogin/res/values-ca/strings.xml
index a2c9ed8..557c3f4 100644
--- a/packages/CaptivePortalLogin/res/values-ca/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ca/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La xarxa a què et vols connectar té problemes de seguretat."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Per exemple, la pàgina d\'inici de sessió podria no pertànyer a l\'organització que es mostra."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continua igualment mitjançant el navegador"</string>
- <string name="ok" msgid="1509280796718850364">"D\'acord"</string>
- <string name="page_info" msgid="4048529256302257195">"Informació de la pàgina"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adreça:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertiment de seguretat"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualitza el certificat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Aquest certificat no és d\'una autoritat de confiança."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nom del lloc no coincideix amb el del certificat."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Aquest certificat ha caducat."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Aquest certificat encara no és vàlid."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Aquest certificat té una data no vàlida."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Aquest certificat no és vàlid."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificat desconegut."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Aquest certificat no és d\'una autoritat de confiança."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"El nom del lloc web no coincideix amb el que inclou el certificat."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Aquest certificat ha caducat."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Aquest certificat encara no és vàlid."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Aquest certificat té una data no vàlida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Aquest certificat no és vàlid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"S\'ha produït un error desconegut de certificat."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertiment de seguretat"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Mostra el certificat"</string>
+ <string name="ok" msgid="2817931639040794018">"D\'acord"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adreça:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informació de la pàgina"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-cs/strings.xml b/packages/CaptivePortalLogin/res/values-cs/strings.xml
index be649a5..0328c76 100644
--- a/packages/CaptivePortalLogin/res/values-cs/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-cs/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Síť, ke které se pokoušíte připojit, má bezpečnostní problémy."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Například přihlašovací stránka nemusí patřit do zobrazované organizace."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Přesto pokračovat prostřednictvím prohlížeče"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Informace o stránce"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornění zabezpečení"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobrazit certifikát"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochází od důvěryhodné autority."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Název webu se neshoduje s názvem uvedeným v certifikátu."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Platnost certifikátu vypršela."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát ještě není platný."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Datum tohoto certifikátu není platné."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznámá chyba certifikátu."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Tento certifikát nepochází od důvěryhodné autority."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Název webu se neshoduje s názvem uvedeným v certifikátu."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Platnost certifikátu vypršela."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Tento certifikát ještě není platný."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Datum tohoto certifikátu není platné."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Tento certifikát je neplatný."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Neznámá chyba certifikátu."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Bezpečnostní upozornění"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Zobrazit certifikát"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informace o stránce"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-da/strings.xml b/packages/CaptivePortalLogin/res/values-da/strings.xml
index 8183105..b9cf7fe9 100644
--- a/packages/CaptivePortalLogin/res/values-da/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-da/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Der er sikkerhedsproblemer på det netværk, du forsøger at logge ind på."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Det er f.eks. ikke sikkert, at loginsiden tilhører den anførte organisation."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Fortsæt alligevel via browseren"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Sideoplysninger"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhedsadvarsel"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis certifikat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dette certifikat stammer ikke fra en troværdig autoritet."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på websitet stemmer ikke overens med navnet på certifikatet."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Dette certifikat er udløbet."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dette certifikat er endnu ikke gyldigt."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette certifikat har en ugyldig dato."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette certifikat er ugyldigt."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukendt fejl i certifikatet."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Dette certifikat stammer ikke fra en pålidelig kilde."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Navnet på websitet stemmer ikke overens med navnet på certifikatet."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Dette certifikat er udløbet."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Dette certifikat er ikke gyldigt endnu."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dette certifikat har en ugyldig dato."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Dette certifikat er ugyldigt."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Ukendt certifikatfejl."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sikkerhedsadvarsel"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Se certifikat"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresse:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Sideoplysninger"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-de/strings.xml b/packages/CaptivePortalLogin/res/values-de/strings.xml
index a9b7415..bca956e 100644
--- a/packages/CaptivePortalLogin/res/values-de/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-de/strings.xml
@@ -2,23 +2,23 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Dieses Netzwerk im Istzustand verwenden"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Dieses Netzwerk nicht verwenden"</string>
+ <string name="action_use_network" msgid="6076184727448466030">"Netzwerk im Istzustand verwenden"</string>
+ <string name="action_do_not_use_network" msgid="4577366536956516683">"Netzwerk nicht verwenden"</string>
<string name="action_bar_label" msgid="917235635415966620">"Im Netzwerk anmelden"</string>
<string name="action_bar_title" msgid="5645564790486983117">"In %1$s anmelden"</string>
<string name="ssl_error_warning" msgid="6653188881418638872">"Im Netzwerk, zu dem du eine Verbindung herstellen möchtest, liegen Sicherheitsprobleme vor."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Beispiel: Die Log-in-Seite gehört eventuell nicht zur angezeigten Organisation."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Trotzdem in einem Browser fortfahren"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Seiteninfo"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sicherheitswarnung"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zertifikat ansehen"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dieses Zertifikat wurde nicht von einer vertrauenswürdigen Stelle ausgegeben."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Dieses Zertifikat ist abgelaufen."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dieses Zertifikat ist noch nicht gültig."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dieses Zertifikat weist ein ungültiges Datum auf."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Dieses Zertifikat ist ungültig."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Unbekannter Zertifikatfehler"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Dieses Zertifikat stammt nicht von einer vertrauenswürdigen Stelle."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Der Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Dieses Zertifikat ist abgelaufen."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Dieses Zertifikat ist noch nicht gültig."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dieses Zertifikat hat ein ungültiges Datum."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Dieses Zertifikat ist ungültig."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Unbekannter Zertifikatfehler."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sicherheitswarnung"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Zertifikat ansehen"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresse:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Seiteninformationen"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-el/strings.xml b/packages/CaptivePortalLogin/res/values-el/strings.xml
index 16bf6e2..f4de7c3 100644
--- a/packages/CaptivePortalLogin/res/values-el/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-el/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Παρουσιάζονται προβλήματα ασφάλειας στο δίκτυο στο οποίο προσπαθείτε να συνδεθείτε."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Για παράδειγμα, η σελίδα σύνδεσης ενδέχεται να μην ανήκει στον οργανισμό που εμφανίζεται."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Συνέχεια ούτως ή άλλως μέσω του προγράμματος περιήγησης"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Πληροφορίες σελίδας"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Διεύθυνση:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Προειδοποίηση ασφαλείας"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Προβολή πιστοποιητικού"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Αυτό το πιστοποιητικό δεν προέρχεται από αξιόπιστη αρχή."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Το όνομα του ιστότοπου δεν αντιστοιχεί με το όνομα στο πιστοποιητικό."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Αυτό το πιστοποιητικό έχει λήξει."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Αυτό το πιστοποιητικό δεν είναι έγκυρο ακόμα."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Αυτό το πιστοποιητικό δεν έχει έγκυρη ημερομηνία."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Αυτό το πιστοποιητικό δεν είναι έγκυρο."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Άγνωστο σφάλμα πιστοποιητικού."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Αυτό το πιστοποιητικό δεν προέρχεται από αξιόπιστη αρχή."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Το όνομα του ιστοτόπου δεν αντιστοιχεί στο όνομα στο πιστοποιητικό."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Αυτό το πιστοποιητικό έχει λήξει."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Αυτό το πιστοποιητικό δεν είναι έγκυρο ακόμα."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Αυτό το πιστοποιητικό δεν έχει έγκυρη ημερομηνία."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Αυτό το πιστοποιητικό δεν είναι έγκυρο."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Άγνωστο σφάλμα πιστοποιητικού."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Προειδοποίηση ασφαλείας"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Προβολή πιστοποιητικού"</string>
+ <string name="ok" msgid="2817931639040794018">"ΟΚ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Διεύθυνση:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Πληροφορίες σελίδας"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml b/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml
index 2e8d1f0..9763339 100644
--- a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
<string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Page info"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml
index 2e8d1f0..9763339 100644
--- a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
<string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Page info"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
index f940299..9763339 100644
--- a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
<string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Page info"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Page info"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
index f940299..9763339 100644
--- a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
<string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Page info"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Page info"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml b/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml
index 9a2051f..b9cc419 100644
--- a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"The network you’re trying to join has security issues."</string>
<string name="ssl_error_example" msgid="647898534624078900">"For example, the login page may not belong to the organization shown."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Page info"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
index c011664..d7ddaa7 100644
--- a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas conectarte tiene problemas de seguridad."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos desde el navegador"</string>
- <string name="ok" msgid="1509280796718850364">"Aceptar"</string>
- <string name="page_info" msgid="4048529256302257195">"Información de la página"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no proviene de una autoridad confiable."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el nombre del certificado."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha expirado."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado no proviene de una autoridad confiable."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"El nombre del sitio no coincide con el nombre del certificado."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado venció."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aún no es válido."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"La fecha de este certificado no es válida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado no es válido."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Error de certificado desconocido"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguridad"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
+ <string name="ok" msgid="2817931639040794018">"Aceptar"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Dirección:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Información de la página"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-es/strings.xml b/packages/CaptivePortalLogin/res/values-es/strings.xml
index 65244e7..e10380f 100644
--- a/packages/CaptivePortalLogin/res/values-es/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-es/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas unirte tiene problemas de seguridad."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de inicio de sesión no pertenezca a la organización mostrada."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos a través del navegador"</string>
- <string name="ok" msgid="1509280796718850364">"Aceptar"</string>
- <string name="page_info" msgid="4048529256302257195">"Información de la página"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no procede de una entidad de certificación de confianza."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el del certificado."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha caducado."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado no procede de una entidad de confianza."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"El nombre del sitio web no coincide con el del certificado."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado ha caducado."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aún no es válido."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"La fecha de este certificado no es válida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado no es válido."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Error de certificado desconocido."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguridad"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
+ <string name="ok" msgid="2817931639040794018">"Aceptar"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Dirección:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Información de la página"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-et/strings.xml b/packages/CaptivePortalLogin/res/values-et/strings.xml
index e4c4c98..2894383 100644
--- a/packages/CaptivePortalLogin/res/values-et/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-et/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Võrgul, millega üritate ühenduse luua, on turvaprobleeme."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Näiteks ei pruugi sisselogimisleht kuuluda kuvatavale organisatsioonile."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Jätka siiski brauseris"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Lehe teave"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Aadress:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Turvahoiatus"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Kuva sertifikaat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"See sertifikaat ei pärine usaldusväärselt asutuselt."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Saidi nimi ei vasta sertifikaadil olevale nimele."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"See sertifikaat on aegunud."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"See sertifikaat pole veel kehtiv."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sellel sertifikaadil on kehtetu kuupäev."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"See sertifikaat on kehtetu."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Tundmatu sertifikaadiviga."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"See sertifikaat ei pärine usaldusväärselt asutuselt."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Saidi nimi ei vasta sertifikaadil olevale nimele."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"See sertifikaat on aegunud."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"See sertifikaat pole veel kehtiv."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Sellel sertifikaadil on kehtetu kuupäev."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"See sertifikaat on kehtetu."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Tundmatu sertifikaadiviga."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Turvahoiatus"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Kuva sertifikaat"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Aadress:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Lehe teave"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-eu/strings.xml b/packages/CaptivePortalLogin/res/values-eu/strings.xml
index 8925aac..b7233ab 100644
--- a/packages/CaptivePortalLogin/res/values-eu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-eu/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Erabili nahi duzun sareak segurtasun-arazoak ditu."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Adibidez, baliteke saioa hasteko orria adierazitako erakundearena ez izatea."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Jarraitu arakatzailearen bidez, halere"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ziurtagiria ez da autoritate fidagarri batena."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Webgunearen izena ez dator bat ziurtagirian agertzen den izenarekin."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Ziurtagiria iraungi da."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ziurtagiriak ez du balio oraindik."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Ziurtagiriaren datak ez du balio."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Ziurtagiriak ez du balio."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Errore ezezagun bat gertatu da ziurtagiriarekin."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Segurtasun-abisua"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ikusi ziurtagiria"</string>
+ <string name="ok" msgid="2817931639040794018">"Ados"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Helbidea:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Orriari buruzko informazioa"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fa/strings.xml b/packages/CaptivePortalLogin/res/values-fa/strings.xml
index 27b9b7f..db9f7e8 100644
--- a/packages/CaptivePortalLogin/res/values-fa/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fa/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"شبکهای که میخواهید به آن بپیوندید مشکلات امنیتی دارد."</string>
<string name="ssl_error_example" msgid="647898534624078900">"به عنوان مثال، صفحه ورود به سیستم ممکن است متعلق به سازمان نشان داده شده نباشد."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"در هر صورت از طریق مرورگر ادامه یابد"</string>
- <string name="ok" msgid="1509280796718850364">"تأیید"</string>
- <string name="page_info" msgid="4048529256302257195">"اطلاعات صفحه"</string>
- <string name="page_info_address" msgid="2222306609532903254">"آدرس:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"اخطار امنیتی"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"مشاهده گواهی"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"این گواهی از یک منبع مورد اطمینان صادر نشده است."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"نام سایت با نام موجود در گواهی مطابقت ندارد."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"این گواهی منقضی شده است."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"این گواهی هنوز معتبر نیست."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تاریخ این گواهی نامعتبر است."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"این گواهی نامعتبر است."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"خطای ناشناخته در گواهی."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"این گواهی از یک منبع مورداطمینان صادر نشده است."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"نام سایت با نام موجود در گواهی مطابقت ندارد."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"این گواهی منقضی شده است."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"این گواهی هنوز معتبر نیست."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"تاریخ این گواهی نامعتبر است."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"این گواهی نامعتبر است."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"خطای ناشناخته در گواهی."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"اخطار امنیتی"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"مشاهده گواهی"</string>
+ <string name="ok" msgid="2817931639040794018">"تأیید"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"نشانی:"</string>
+ <string name="page_info" msgid="4416941086705172545">"اطلاعات صفحه"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fi/strings.xml b/packages/CaptivePortalLogin/res/values-fi/strings.xml
index 8086fbf..1d7fcb0 100644
--- a/packages/CaptivePortalLogin/res/values-fi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fi/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Verkossa, johon yrität muodostaa yhteyttä, on turvallisuusongelmia."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Kirjautumissivu ei välttämättä kuulu näytetylle organisaatiolle."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Jatka silti selaimen kautta."</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Sivun tiedot"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Osoite:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Suojausvaroitus"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Näytä varmenne"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Varmenteen myöntäjä ei ole luotettava taho."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sivuston nimi ei vastaa varmenteessa olevaa nimeä."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Varmenne ei ole enää voimassa."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Varmenne ei ole vielä voimassa."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Varmenteen päiväys ei kelpaa."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Varmenne on virheellinen."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Tuntematon varmennevirhe."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Varmenteen myöntäjä ei ole luotettava taho."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Sivuston nimi ei vastaa varmenteessa olevaa nimeä."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Varmenne ei ole enää voimassa."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Varmenne ei ole vielä voimassa."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Varmenteen päiväys ei kelpaa."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Varmenne on virheellinen."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Tuntematon varmennevirhe."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Suojausvaroitus"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Näytä varmenne"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Osoite:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Sivun tiedot"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml
index a7525a5..979e236 100644
--- a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion pourrait ne pas appartenir à l\'organisation représentée."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans un navigateur"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ce certificat ne provient pas d\'une autorité de confiance."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Le nom du site ne correspond pas au nom figurant sur le certificat."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Ce certificat a expiré."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ce certificat n\'est pas encore valide."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Ce certificat contient une date non valide."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Ce certificat n\'est pas valide."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Erreur de certificat inconnue."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avertissement de sécurité"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Afficher le certificat"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresse :"</string>
+ <string name="page_info" msgid="4416941086705172545">"Renseignements sur la page"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fr/strings.xml b/packages/CaptivePortalLogin/res/values-fr/strings.xml
index 39fc569..e348a4d 100644
--- a/packages/CaptivePortalLogin/res/values-fr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fr/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion peut ne pas appartenir à l\'organisation représentée."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans le navigateur"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Infos sur la page"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresse :"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertissement de sécurité"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Afficher le certificat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ce certificat provient d\'une autorité non approuvée."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Le nom du site ne correspond pas au nom indiqué dans le certificat."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Le certificat a expiré."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ce certificat n\'est pas encore valide."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La date de ce certificat n\'est pas valide."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Ce certificat n\'est pas valide."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Erreur : Certificat inconnu."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ce certificat provient d\'une autorité non approuvée."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Le nom du site ne correspond pas au nom indiqué dans le certificat."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Ce certificat a expiré."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ce certificat n\'est pas encore valide."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"La date de ce certificat n\'est pas valide."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Ce certificat n\'est pas valide."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Erreur de certificat inconnue."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avertissement de sécurité"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Afficher le certificat"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresse :"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informations sur la page"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-gl/strings.xml b/packages/CaptivePortalLogin/res/values-gl/strings.xml
index 6578285..6419516 100644
--- a/packages/CaptivePortalLogin/res/values-gl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-gl/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"A rede á que tentas unirte ten problemas de seguranza."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, é posible que a páxina de inicio de sesión non pertenza á organización que se mostra."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar igualmente co navegador"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado non procede dunha autoridade de confianza."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do sitio non coincide co nome que aparece no certificado."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado caducou."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aínda non é válido."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado ten unha data non válida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado non é válido."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Produciuse un erro descoñecido relacionado co certificado."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguranza"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
+ <string name="ok" msgid="2817931639040794018">"Aceptar"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Enderezo:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Información da páxina"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-gu/strings.xml b/packages/CaptivePortalLogin/res/values-gu/strings.xml
index c15eca4..aef043f 100644
--- a/packages/CaptivePortalLogin/res/values-gu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-gu/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"તમે જોડાવાનો પ્રયાસ કરી રહ્યાં છો તે નેટવર્કમાં સુરક્ષા સમસ્યાઓ છે."</string>
<string name="ssl_error_example" msgid="647898534624078900">"ઉદાહરણ તરીકે, લોગિન પૃષ્ઠ દર્શાવેલ સંસ્થાનું હોઈ શકતું નથી."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"બ્રાઉઝર મારફતે કોઈપણ રીતે ચાલુ રાખો"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"આ પ્રમાણપત્ર વિશ્વસનીય સત્તાધિકારી તરફથી મળ્યું નથી."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"સાઇટનું નામ એ પ્રમાણપત્ર પર રહેલાં નામ સાથે મેળ ખાતું નથી."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"આ પ્રમાણપત્રની સમયસીમા સમાપ્ત થઈ ગઈ છે."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"આ પ્રમાણપત્ર હજુ માન્ય નથી."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"આ પ્રમાણપત્રમાં અમાન્ય તારીખ છે."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"આ પ્રમાણપત્ર અમાન્ય છે."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"પ્રમાણપત્રમાં અજાણી ભૂલ."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"સુરક્ષા વિશે ચેતવણી"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"પ્રમાણપત્ર જુઓ"</string>
+ <string name="ok" msgid="2817931639040794018">"ઓકે"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"સરનામું:"</string>
+ <string name="page_info" msgid="4416941086705172545">"પેજ વિશે માહિતી"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hi/strings.xml b/packages/CaptivePortalLogin/res/values-hi/strings.xml
index d924fff..b8ae82a 100644
--- a/packages/CaptivePortalLogin/res/values-hi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hi/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"आप जिस नेटवर्क में शामिल होने का प्रयास कर रहे हैं उसमें सुरक्षा समस्याएं हैं."</string>
<string name="ssl_error_example" msgid="647898534624078900">"उदाहरण के लिए, हो सकता है कि लॉगिन पृष्ठ दिखाए गए संगठन से संबद्ध ना हो."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउज़र के द्वारा फिर जारी रखें"</string>
- <string name="ok" msgid="1509280796718850364">"ठीक"</string>
- <string name="page_info" msgid="4048529256302257195">"पृष्ठ जानकारी"</string>
- <string name="page_info_address" msgid="2222306609532903254">"पता:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"सुरक्षा चेतावनी"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"प्रमाणपत्र देखें"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"यह प्रमाणपत्र किसी विश्वस्त प्राधिकारी का नहीं है."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"साइट का नाम, प्रमाणपत्र के नाम से मिलान नहीं करता."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"इस प्रमाणपत्र की समय सीमा समाप्त हो गई है."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"यह प्रमाणपत्र अभी तक मान्य नहीं है."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"इस प्रमाणपत्र में एक अमान्य दिनांक है."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"यह प्रमाणपत्र अमान्य है."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"अज्ञात प्रमाणपत्र त्रुटि."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"यह प्रमाणपत्र किसी भरोसेमंद अधिकारी का नहीं है."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"साइट के नाम का मिलान प्रमाणपत्र में दिए नाम से नहीं हो रहा है."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"इस प्रमाणपत्र की समय-सीमा खत्म हो चुकी है."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"यह प्रमाणपत्र अभी तक मान्य नहीं है."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"इस प्रमाणपत्र की तारीख गलत है."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"यह प्रमाणपत्र अमान्य है."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"प्रमाणपत्र में अनजान गड़बड़ी."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"सुरक्षा चेतावनी"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"प्रमाणपत्र देखें"</string>
+ <string name="ok" msgid="2817931639040794018">"ठीक है"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"पता:"</string>
+ <string name="page_info" msgid="4416941086705172545">"पेज की जानकारी"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hr/strings.xml b/packages/CaptivePortalLogin/res/values-hr/strings.xml
index 11b1dd3..f6adcdb 100644
--- a/packages/CaptivePortalLogin/res/values-hr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hr/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj se pokušavate pridružiti ima sigurnosne poteškoće."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi putem preglednika"</string>
- <string name="ok" msgid="1509280796718850364">"U redu"</string>
- <string name="page_info" msgid="4048529256302257195">"Informacije o stranici"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozorenje o sigurnosti"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži certifikat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ovaj certifikat ne potječe iz pouzdanog izvora."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Naziv web-lokacije ne podudara se s nazivom na certifikatu."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Ovaj je certifikat istekao."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ovaj certifikat još nije važeći."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ovaj certifikat ima nevažeći datum."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Ovaj certifikat nije valjan."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Nepoznata pogreška certifikata."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ovaj certifikat ne potječe iz pouzdanog izvora."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Naziv web-lokacije ne podudara se s nazivom na certifikatu."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Ovaj je certifikat istekao."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ovaj certifikat još nije važeći."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Ovaj certifikat ima nevažeći datum."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Ovaj certifikat nije važeći."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Nepoznata pogreška certifikata."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sigurnosno upozorenje"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Pregledajte certifikat"</string>
+ <string name="ok" msgid="2817931639040794018">"U redu"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informacije o stranici"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hu/strings.xml b/packages/CaptivePortalLogin/res/values-hu/strings.xml
index 145e2ab..ab289d1 100644
--- a/packages/CaptivePortalLogin/res/values-hu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hu/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Biztonsági problémák vannak azzal a hálózattal, amelyhez csatlakozni szeretne."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Például lehet, hogy a bejelentkezési oldal nem a megjelenített szervezethez tartozik."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Folytatás ennek ellenére böngészőn keresztül"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Oldaladatok"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Cím:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Biztonsági figyelmeztetés"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tanúsítvány megtekintése"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ez a tanúsítvány nem hiteles tanúsítványkibocsátótól származik."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"A webhely neve nem egyezik a tanúsítványon lévő névvel."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"A tanúsítvány lejárt."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"A tanúsítvány még nem érvényes."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"A tanúsítvány dátuma érvénytelen."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Ez a tanúsítvány érvénytelen."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Ismeretlen tanúsítványhiba."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ez a tanúsítvány nem hiteles tanúsítványkibocsátótól származik."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"A webhely neve nem egyezik a tanúsítványon lévő névvel."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"A tanúsítvány lejárt."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"A tanúsítvány még nem érvényes."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"A tanúsítvány dátuma érvénytelen."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Ez a tanúsítvány érvénytelen."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Ismeretlen tanúsítványhiba."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Biztonsági figyelmeztetés"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Tanúsítvány megtekintése"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Cím:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Oldaladatok"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hy/strings.xml b/packages/CaptivePortalLogin/res/values-hy/strings.xml
index a0ee862..d859d90 100644
--- a/packages/CaptivePortalLogin/res/values-hy/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hy/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Ցանցը, որին փորձում եք միանալ, անվտանգության խնդիրներ ունի:"</string>
<string name="ssl_error_example" msgid="647898534624078900">"Օրինակ՝ մուտքի էջը կարող է ցուցադրված կազմակերպության էջը չլինել:"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Շարունակել այնուամենայնիվ դիտարկիչի միջոցով"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Հավաստագրի աղբյուրը վստահելի չէ:"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Կայքի անունը չի համապատասխանում հավաստագրում նշված անվանը:"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Հավաստագրի ժամկետը լրացել է:"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Հավաստագիրը դեռ վավեր չէ:"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Հավաստագրի ամսաթիվն անվավեր է:"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Հավաստագիրն անվավեր է:"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Հավաստագրի հետ կապված անհայտ սխալ առաջացավ:"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Զգուշացում"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Դիտել հավաստագիրը"</string>
+ <string name="ok" msgid="2817931639040794018">"Եղավ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Հասցե՝"</string>
+ <string name="page_info" msgid="4416941086705172545">"Էջի մասին"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml
index 4a335dd..7036318 100644
--- a/packages/CaptivePortalLogin/res/values-in/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-in/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Jaringan yang ingin Anda masuki mengalami masalah keamanan."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Tetap lanjutkan melalui browser"</string>
- <string name="ok" msgid="1509280796718850364">"Oke"</string>
- <string name="page_info" msgid="4048529256302257195">"Info laman"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Peringatan sertifikat"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sertifikat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikat ini tidak berasal dari otoritas tepercaya."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama situs tidak cocok dengan nama pada sertifikat."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikat ini telah kedaluwarsa."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikat ini belum valid."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tanggal sertifikat ini tidak valid."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Sertifikat ini tidak valid."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Kesalahan sertifikat tak dikenal."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Sertifikat ini tidak berasal dari otoritas tepercaya."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nama situs tidak cocok dengan nama pada sertifikat."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Masa berlaku sertifikat ini telah berakhir."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Sertifikat ini belum valid."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Tanggal sertifikat ini tidak valid."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Sertifikat ini tidak valid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Error sertifikat tak dikenal."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Peringatan keamanan"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Lihat sertifikat"</string>
+ <string name="ok" msgid="2817931639040794018">"Oke"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Alamat:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Info halaman"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-is/strings.xml b/packages/CaptivePortalLogin/res/values-is/strings.xml
index 8fde24b..7c2a5e4 100644
--- a/packages/CaptivePortalLogin/res/values-is/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-is/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Öryggisvandamál eru á netinu sem þú ert að reyna að tengjast."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Til dæmis getur verið að innskráningarsíðan tilheyri ekki fyrirtækinu sem birtist."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Halda samt áfram í vafra"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Þetta vottorð er ekki frá traustum aðila."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nafnið á síðunni passar ekki við nafnið á vottorðinu."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Þetta vottorð er útrunnið."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Þetta vottorð hefur ekki enn tekið gildi."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Þetta vottorð er með ógilda dagsetningu."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Þetta vottorð er ógilt."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Óþekkt vottorðsvilla."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Öryggisviðvörun"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Skoða vottorð"</string>
+ <string name="ok" msgid="2817931639040794018">"Í lagi"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Netfang:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Upplýsingar um síðu"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-it/strings.xml b/packages/CaptivePortalLogin/res/values-it/strings.xml
index 2cc4038..3896cb4 100644
--- a/packages/CaptivePortalLogin/res/values-it/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-it/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"La rete a cui stai tentando di accedere presenta problemi di sicurezza."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Ad esempio, la pagina di accesso potrebbe non appartenere all\'organizzazione indicata."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continua comunque dal browser"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Info pagina"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Indirizzo:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avviso di sicurezza"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizza certificato"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Questo certificato non proviene da un\'autorità attendibile."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Il nome del sito non corrisponde al nome nel certificato."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Il certificato è scaduto."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Questo certificato non è ancora valido."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Questo certificato presenta una data non valida."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Questo certificato non è valido."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Errore certificato sconosciuto."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Questo certificato non proviene da un\'autorità attendibile."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Il nome del sito non corrisponde al nome nel certificato."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Il certificato è scaduto."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Questo certificato non è ancora valido."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Questo certificato presenta una data non valida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Questo certificato non è valido."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Errore certificato sconosciuto."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avviso di sicurezza"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Visualizza certificato"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Indirizzo:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informazioni sulla pagina"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-iw/strings.xml b/packages/CaptivePortalLogin/res/values-iw/strings.xml
index 527e692..4ce98ee 100644
--- a/packages/CaptivePortalLogin/res/values-iw/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-iw/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"יש בעיות אבטחה ברשת שאליה אתה מנסה להתחבר."</string>
<string name="ssl_error_example" msgid="647898534624078900">"לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"המשך בכל זאת באמצעות דפדפן"</string>
- <string name="ok" msgid="1509280796718850364">"אישור"</string>
- <string name="page_info" msgid="4048529256302257195">"פרטי דף"</string>
- <string name="page_info_address" msgid="2222306609532903254">"כתובת:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"אזהרת אבטחה"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"הצג אישור"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"אישור זה אינו מגיע מרשות אמינה."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"שם האתר לא תואם לשם באישור."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"פג תוקפו של אישור זה."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"אישור זה אינו חוקי עדיין."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"לאישור זה יש תאריך בלתי חוקי."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"אישור זה אינו חוקי."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"שגיאת אישור לא ידועה."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"אישור זה אינו מגיע מרשות אמינה."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"שם האתר לא תואם לשם באישור."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"פג תוקפו של אישור זה."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"אישור זה אינו חוקי עדיין."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"לאישור זה יש תאריך בלתי חוקי."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"אישור זה אינו חוקי."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"שגיאת אישור לא ידועה."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"אזהרת אבטחה"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"הצגת אישור"</string>
+ <string name="ok" msgid="2817931639040794018">"אישור"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"כתובת:"</string>
+ <string name="page_info" msgid="4416941086705172545">"פרטי דף"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ja/strings.xml b/packages/CaptivePortalLogin/res/values-ja/strings.xml
index bcc8686..bab9026 100644
--- a/packages/CaptivePortalLogin/res/values-ja/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ja/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"接続しようとしているネットワークにセキュリティの問題があります。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"たとえば、ログインページが表示されている組織に属していない可能性があります。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ブラウザから続行"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"ページ情報"</string>
- <string name="page_info_address" msgid="2222306609532903254">"アドレス:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"セキュリティ警告"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"証明書を表示"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"この証明書は信頼できる認証機関のものではありません。"</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"サイト名と証明書上の名前が一致しません。"</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"この証明書は有効期限切れです。"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"この証明書はまだ有効ではありません。"</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"この証明書の日付は無効です。"</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"この証明書は無効です。"</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"不明な証明書エラーです。"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"この証明書は信頼できる認証機関のものではありません。"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"サイト名と証明書上の名前が一致しません。"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"この証明書は有効期限切れです。"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"この証明書はまだ有効ではありません。"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"この証明書の日付は無効です。"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"この証明書は無効です。"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"不明な証明書エラーです。"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"セキュリティに関する警告"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"証明書を表示"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"メールアドレス:"</string>
+ <string name="page_info" msgid="4416941086705172545">"ページ情報"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ka/strings.xml b/packages/CaptivePortalLogin/res/values-ka/strings.xml
index 1ccff12..ec820e5 100644
--- a/packages/CaptivePortalLogin/res/values-ka/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ka/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ქსელს, რომელზედაც მიერთებას ცდილობთ, უსაფრთხოების პრობლემები აქვს."</string>
<string name="ssl_error_example" msgid="647898534624078900">"მაგალითად, სისტემაში შესვლის გვერდი შეიძლება არ ეკუთვნოდეს ნაჩვენებ ორგანიზაციას."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ბრაუზერში გაგრძელება"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ეს სერტიფიკატი არ არის გაცემული ნდობით აღჭურვილი ორგანოს მიერ."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"საიტის სახელი არ ემთხვევა სერტიფიკატში მითითებულს."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ეს სერტიფიკატი ვადაგასულია."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ეს სერტიფიკატი ჯერ არ არის ძალაში."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ამ სერტიფიკატში მითითებულია არასწორი თარიღი."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ეს სერტიფიკატი არასწორია."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"წარმოიშვა სერტიფიკატთან დაკავშირებული უცნობი შეცდომა."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"გაფრთხილება უსაფრთხოების შესახებ"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"სერტიფიკატის ნახვა"</string>
+ <string name="ok" msgid="2817931639040794018">"კარგი"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"მისამართი:"</string>
+ <string name="page_info" msgid="4416941086705172545">"გვერდის ინფორმაცია"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-kk/strings.xml b/packages/CaptivePortalLogin/res/values-kk/strings.xml
index a904dea..a23eafd 100644
--- a/packages/CaptivePortalLogin/res/values-kk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-kk/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Қосылайын деп жатқан желіңіз қауіпсіз болуы мүмкін."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Мысалы, кіру беті көрсетілген ұйымға тиесілі болмауы мүмкін."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Бәрібір браузер арқылы жалғастыру"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Сертификатты сенімді орган бермеген."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Сайттың атауы мен сертификатта көрсетілген атау сәйкес келмейді."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Сертификаттың жарамдылық мерзімі аяқталған."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Сертификат әлі жарамсыз."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Сертификат күні жарамсыз."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Сертификат жарамсыз."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Сертификатқа қатысты белгісіз қате."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Қауіпсіздік туралы ескерту"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Сертификатты көру"</string>
+ <string name="ok" msgid="2817931639040794018">"Жарайды"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Мекенжай:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Бет туралы ақпарат"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-km/strings.xml b/packages/CaptivePortalLogin/res/values-km/strings.xml
index a0497f8..b4274e9 100644
--- a/packages/CaptivePortalLogin/res/values-km/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-km/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"បណ្តាញដែលអ្នកកំពុងព្យាយាមចូលមានបញ្ហាសុវត្ថិភាព។"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ឧបករណ៍៖ ទំព័រចូលនេះអាចនឹងមិនមែនជាកម្មសិទ្ធិរបស់ស្ថាប័នដែលបានបង្ហាញនេះទេ។"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"យ៉ាងណាក៏ដោយនៅតែបន្តតាមរយៈកម្មវិធីរុករក"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"វិញ្ញាបនបត្រនេះមិនបានចេញដោយអាជ្ញាធរដែលគួរឱ្យទុកចិត្តទេ។"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ឈ្មោះគេហទំព័រនេះមិនត្រូវនឹងឈ្មោះនៅលើវិញ្ញាបនបត្រទេ។"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"វិញ្ញាបនបត្រនេះផុតកំណត់ហើយ។"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"វិញ្ញាបនបត្រនេះមិនទាន់មានសុពលភាពនៅឡើយទេ។"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"វិញ្ញាបនបត្រនេះមានកាលបរិច្ឆេទដែលគ្មានសុពលភាព។"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"វិញ្ញាបនបត្រនេះគ្មានសុពលភាពទេ។"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"មានបញ្ហាដោយសារមិនស្គាល់វិញ្ញាបនបត្រ។"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"ការព្រមានផ្នែកសុវត្ថិភាព"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"មើលវិញ្ញាបនបត្រ"</string>
+ <string name="ok" msgid="2817931639040794018">"យល់ព្រម"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"អាសយដ្ឋាន៖"</string>
+ <string name="page_info" msgid="4416941086705172545">"ព័ត៌មានទំព័រ"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-kn/strings.xml b/packages/CaptivePortalLogin/res/values-kn/strings.xml
index 3084504..731bbcc 100644
--- a/packages/CaptivePortalLogin/res/values-kn/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-kn/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ನೀವು ಸೇರಬೇಕೆಂದಿರುವ ನೆಟ್ವರ್ಕ್ ಭದ್ರತೆ ಸಮಸ್ಯೆಗಳನ್ನು ಹೊಂದಿದೆ."</string>
<string name="ssl_error_example" msgid="647898534624078900">"ಉದಾಹರಣೆಗೆ, ಲಾಗಿನ್ ಪುಟವು ತೋರಿಸಲಾಗಿರುವ ಸಂಸ್ಥೆಗೆ ಸಂಬಂಧಿಸಿರುವುದಿಲ್ಲ."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ಹೇಗಾದರೂ ಬ್ರೌಸರ್ ಮೂಲಕ ಮುಂದುವರಿಸಿ"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ಈ ಪ್ರಮಾಣಪತ್ರವು ವಿಶ್ವಾಸಾರ್ಹ ಪ್ರಾಧಿಕಾರದಿಂದ ಬಂದಿಲ್ಲ."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ಸೈಟ್ನ ಹೆಸರು ಪ್ರಮಾಣಪತ್ರದಲ್ಲಿನ ಹೆಸರಿನ ಜೊತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುತ್ತಿಲ್ಲ."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ಈ ಪ್ರಮಾಣಪತ್ರದ ಅವಧಿ ಮೀರಿದೆ."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ಈ ಪ್ರಮಾಣಪತ್ರವು ಇನ್ನೂ ಮಾನ್ಯವಾಗಿಲ್ಲ."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ಈ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾದ ದಿನಾಂಕವನ್ನು ಹೊಂದಿದೆ."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ಈ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾಗಿದೆ."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"ಪ್ರಮಾಣಪತ್ರದ ಅಜ್ಞಾತ ದೋಷ."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"ಭದ್ರತಾ ಎಚ್ಚರಿಕೆ"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ಪ್ರಮಾಣಪತ್ರವನ್ನು ವೀಕ್ಷಿಸಿ"</string>
+ <string name="ok" msgid="2817931639040794018">"ಸರಿ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ವಿಳಾಸ:"</string>
+ <string name="page_info" msgid="4416941086705172545">"ಪುಟದ ಮಾಹಿತಿ"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ko/strings.xml b/packages/CaptivePortalLogin/res/values-ko/strings.xml
index 7a7f7e0..8c2d9d2 100644
--- a/packages/CaptivePortalLogin/res/values-ko/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ko/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"가입하려는 네트워크에 보안 문제가 있습니다."</string>
<string name="ssl_error_example" msgid="647898534624078900">"예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"브라우저를 통해 계속하기"</string>
- <string name="ok" msgid="1509280796718850364">"확인"</string>
- <string name="page_info" msgid="4048529256302257195">"페이지 정보"</string>
- <string name="page_info_address" msgid="2222306609532903254">"주소:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"보안 경고"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"인증서 보기"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"신뢰할 수 있는 인증 기관에서 발급한 인증서가 아닙니다."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"사이트 이름이 인증서에 있는 것과 일치하지 않습니다."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"인증서가 만료되었습니다."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"인증서가 아직 유효하지 않습니다."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"인증서 날짜가 유효하지 않습니다."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"인증서가 잘못되었습니다."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"알 수 없는 인증서 오류입니다."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"신뢰할 수 있는 인증 기관에서 발급한 인증서가 아닙니다."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"사이트 이름이 인증서상의 이름과 일치하지 않습니다."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"인증서가 만료되었습니다."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"인증서가 아직 유효하지 않습니다."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"인증서의 날짜가 잘못되었습니다."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"인증서가 잘못되었습니다."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"알 수 없는 인증서 오류입니다."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"보안 경고"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"인증서 보기"</string>
+ <string name="ok" msgid="2817931639040794018">"확인"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"주소:"</string>
+ <string name="page_info" msgid="4416941086705172545">"페이지 정보"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ky/strings.xml b/packages/CaptivePortalLogin/res/values-ky/strings.xml
index af81ce3..6d663cb 100644
--- a/packages/CaptivePortalLogin/res/values-ky/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ky/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Кошулайын деген тармагыңызда коопсуздук көйгөйлөрү бар."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Мисалы, каттоо эсебине кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Баары бир серепчи аркылуу улантуу"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Бул тастыктама ишеничтүү мекеме аркылуу берилген эмес."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Сайттын аталышы тастыктаманын аталышына дал келбейт."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Бул тастыктаманын мөөнөтү өтүп кеткен."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Бул тастыктама азырынча жараксыз."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Бул тастыктаманын күнү жараксыз."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Бул тастыктама жараксыз."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Тастыктамага байланыштуу белгисиз ката."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Коопсуздук эскертүүсү"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Тастыктаманы көрүү"</string>
+ <string name="ok" msgid="2817931639040794018">"ЖАРАЙТ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Дарек:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Барак жөнүндө маалымат"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lo/strings.xml b/packages/CaptivePortalLogin/res/values-lo/strings.xml
index ee2b263..673bf2c 100644
--- a/packages/CaptivePortalLogin/res/values-lo/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-lo/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ເຄືອຂ່າຍທີ່ທ່ານກຳລັງເຂົ້າຮ່ວມມີບັນຫາຄວາມປອດໄພ."</string>
<string name="ssl_error_example" msgid="647898534624078900">"ຕົວຢ່າງ, ໜ້າລົງຊື່ເຂົ້າໃຊ້ອາດຈະບໍ່ເປັນຂອງອົງການທີ່ສະແດງຂຶ້ນ."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ແນວໃດກໍ່ສືບຕໍ່ຜ່ານບຣາວເຊີ"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ໃບຮັບຮອງນີ້ບໍ່ໄດ້ມາຈາກໜ່ວຍງານທີ່ເຊື່ອຖືໄດ້."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ຊື່ຂອງເວັບໄຊບໍ່ກົງກັບຊື່ຢູ່ໃບຮັບຮອງ."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ໃບຮັບຮອງນີ້ຫມົດອາຍຸແລ້ວ."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ໃບຮັບຮອງນີ້ບໍ່ຖືກຕ້ອງເທື່ອ."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ໃບຮັບຮອງນີ້ມີວັນທີບໍ່ຖືກຕ້ອງ."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ໃບຮັບຮອງນີ້ບໍ່ຖືກຕ້ອງ."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"ໃບຮັບຮອງມີຄວາມຜິດພາດທີ່ບໍ່ຮູ້ຈັກ."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"ຄຳເຕືອນຄວາມປອດໄພ"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ເບິ່ງໃບຮັບຮອງ"</string>
+ <string name="ok" msgid="2817931639040794018">"ຕົກລົງ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ທີ່ຢູ່:"</string>
+ <string name="page_info" msgid="4416941086705172545">"ຂໍ້ມູນໜ້າ"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lt/strings.xml b/packages/CaptivePortalLogin/res/values-lt/strings.xml
index 158f7ce..eb4c46c 100644
--- a/packages/CaptivePortalLogin/res/values-lt/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-lt/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Kilo tinklo, prie kurio bandote prisijungti, problemų."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Vis tiek tęsti naudojant naršyklę"</string>
- <string name="ok" msgid="1509280796718850364">"Gerai"</string>
- <string name="page_info" msgid="4048529256302257195">"Puslapio informacija"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresas:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Saugos įspėjimas"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Žiūrėti sertifikatą"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šį sertifikatą išdavė nepatikima įstaiga."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Svetainės pavadinimas neatitinka sertifikate nurodyto pavadinimo."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Šio sertifikato galiojimo laikas baigėsi."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikatas dar negalioja."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šio sertifikato data netinkama."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikatas netinkamas."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Nežinoma sertifikato klaida."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Šį sertifikatą išdavė nepatikima įstaiga."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Svetainės pavadinimas neatitinka sertifikate nurodyto pavadinimo."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Šio sertifikato galiojimo laikas baigėsi."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Šis sertifikatas dar negalioja."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Šio sertifikato data netinkama."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Šis sertifikatas netinkamas."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Nežinoma sertifikato klaida."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Saugos įspėjimas"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Žiūrėti sertifikatą"</string>
+ <string name="ok" msgid="2817931639040794018">"Gerai"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresas"</string>
+ <string name="page_info" msgid="4416941086705172545">"Puslapio informacija"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lv/strings.xml b/packages/CaptivePortalLogin/res/values-lv/strings.xml
index a42cb22..5554343 100644
--- a/packages/CaptivePortalLogin/res/values-lv/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-lv/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Tīklam, kuram mēģināt pievienoties, ir drošības problēmas."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Piemēram, pieteikšanās lapa, iespējams, nepieder norādītajai organizācijai."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Tik un tā turpināt, izmantojot pārlūkprogrammu"</string>
- <string name="ok" msgid="1509280796718850364">"Labi"</string>
- <string name="page_info" msgid="4048529256302257195">"Lapas informācija"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adrese:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Drošības brīdinājums"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Skatīt sertifikātu"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šo sertifikātu nav izsniegusi uzticama iestāde."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Vietnes nosaukums neatbilst nosaukumam sertifikātā."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Šī sertifikāta derīguma termiņš ir beidzies."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikāts vēl nav derīgs."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šī sertifikāta datums nav derīgs."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikāts nav derīgs."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Nezināma sertifikāta kļūda."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Šo sertifikātu nav izsniegusi uzticama iestāde."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Vietnes nosaukums neatbilst nosaukumam sertifikātā."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Šī sertifikāta derīguma termiņš ir beidzies."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Šis sertifikāts vēl nav derīgs."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Šī sertifikāta datums nav derīgs."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Šis sertifikāts nav derīgs."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Nezināma sertifikāta kļūda."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Drošības brīdinājums"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Skatīt sertifikātu"</string>
+ <string name="ok" msgid="2817931639040794018">"Labi"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adrese:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Lapas informācija"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-mk/strings.xml b/packages/CaptivePortalLogin/res/values-mk/strings.xml
index 2ae32c8..9a57fca 100644
--- a/packages/CaptivePortalLogin/res/values-mk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-mk/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата на која се обидувате да се придружите има проблеми со безбедноста."</string>
<string name="ssl_error_example" msgid="647898534624078900">"На пример, страницата за најавување може да не припаѓа на организацијата што е прикажана."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Сепак продолжи преку прелистувач"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Сертификатов не е од доверлив орган."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Името на сајтот не се совпаѓа со името на сертификатот."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Сертификатов е истечен."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Сертификатов сѐ уште не е важечки."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Сертификатов има неважечки датум."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Сертификатов е неважечки."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Непозната грешка на сертификатот."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Предупредување за безбедност"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Прикажи го сертификатот"</string>
+ <string name="ok" msgid="2817931639040794018">"Во ред"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Адреса:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Информации за страницата"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ml/strings.xml b/packages/CaptivePortalLogin/res/values-ml/strings.xml
index 79551f8..8fd74d1 100644
--- a/packages/CaptivePortalLogin/res/values-ml/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ml/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"നിങ്ങൾ ചേരാൻ ശ്രമിക്കുന്ന നെറ്റ്വർക്കിൽ സുരക്ഷാ പ്രശ്നങ്ങളുണ്ടായിരിക്കാം."</string>
<string name="ssl_error_example" msgid="647898534624078900">"ഉദാഹരണത്തിന്, കാണിച്ചിരിക്കുന്ന ഓർഗനൈസേഷന്റേതായിരിക്കില്ല ലോഗിൻ പേജ്."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"എന്തായാലും ബ്രൗസർ വഴി തുടരുക"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ഈ സർട്ടിഫിക്കറ്റ്, വിശ്വസനീയ അതോറിറ്റിയിൽ നിന്നുള്ളതല്ല."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"സൈറ്റിന്റെ പേര്, സർട്ടിഫിക്കറ്റിലുള്ള പേരുമായി പൊരുത്തപ്പെടുന്നില്ല."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ഈ സർട്ടിഫിക്കറ്റ് കാലഹരണപ്പെട്ടു."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ഈ സർട്ടിഫിക്കറ്റിന് ഇപ്പോഴും സാധുതയില്ല."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ഈ സർട്ടിഫിക്കറ്റിൽ അസാധുവായ തീയതിയാണുള്ളത്."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ഈ സർട്ടിഫിക്കറ്റ് അസാധുവാണ്."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"അജ്ഞാത സർട്ടിഫിക്കറ്റ് പിശക്."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"സുരക്ഷാ മുന്നറിയിപ്പ്"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"സർട്ടിഫിക്കറ്റ് കാണുക"</string>
+ <string name="ok" msgid="2817931639040794018">"ശരി"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"വിലാസം:"</string>
+ <string name="page_info" msgid="4416941086705172545">"പേജ് വിവരം"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-mn/strings.xml b/packages/CaptivePortalLogin/res/values-mn/strings.xml
index 67670915..f11f1d6 100644
--- a/packages/CaptivePortalLogin/res/values-mn/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-mn/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Таны нэгдэх гэж буй сүлжээ аюулгүй байдлын асуудалтай байна."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Жишээлбэл нэвтрэх хуудас нь харагдах байгууллагынх биш байж болзошгүй."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Ямартаа ч хөтчөөр үргэлжлүүлэх"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Энэ сертификатыг итгэмжлэгдсэн байгууллагаас олгоогүй байна."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Сайтын нэр сертификат дээрх нэртэй таарахгүй байна."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Энэ сертификатын хугацаа дууссан байна."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Энэ сертификат одоогоор хүчингүй байна."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Энэ сертификатын огноо хүчингүй байна."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Энэ сертификат хүчингүй байна."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Сертификатын үл мэдэгдэх алдаа гарлаа."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Аюулгүй байдлын анхааруулга"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Сертификатыг харах"</string>
+ <string name="ok" msgid="2817931639040794018">"ЗА"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Хаяг:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Хуудасны мэдээлэл"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-mr/strings.xml b/packages/CaptivePortalLogin/res/values-mr/strings.xml
index 6ea9006..0adc92e 100644
--- a/packages/CaptivePortalLogin/res/values-mr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-mr/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ज्या नेटवर्कमध्ये तुम्ही सामील होण्याचा प्रयत्न करीत आहात त्यात सुरक्षितता समस्या आहेत."</string>
<string name="ssl_error_example" msgid="647898534624078900">"उदाहरणार्थ, लॉगिन पृष्ठ कदाचित दर्शविलेल्या संस्थेच्या मालकीचे नसावे."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउझरद्वारे तरीही सुरु ठेवा"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"हे प्रमाणपत्र विश्वसनीय संस्थेने दिलेले नाही"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"साइटचे नाव प्रमाणपत्रावरील नावाशी जुळत नाही."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"हे प्रमाणपत्र एक्स्पायर झाले आहे."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"हे प्रमाणपत्र अजून योग्य नाही."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"या प्रमाणपत्राची तारीख चुकीची आहे."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"हे प्रमाणपत्र चुकीचे आहे."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"अज्ञात प्रमाणपत्र एरर."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"सुरक्षितता चेतावणी"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"प्रमाणपत्र पाहा"</string>
+ <string name="ok" msgid="2817931639040794018">"ठीक आहे"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"पत्ता:"</string>
+ <string name="page_info" msgid="4416941086705172545">"पेज माहिती"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ms/strings.xml b/packages/CaptivePortalLogin/res/values-ms/strings.xml
index aaa51c8..cc4957e 100644
--- a/packages/CaptivePortalLogin/res/values-ms/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ms/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Rangkaian yang anda cuba sertai mempunyai isu keselamatan."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Teruskan juga melalui penyemak imbas"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Maklumat halaman"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Amaran keselamatan"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sijil"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sijil ini bukan daripada pihak berkuasa yang dipercayai."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama tapak tidak sepadan dengan nama pada sijil."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Sijil ini telah tamat tempoh."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sijil ini belum lagi sah."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sijil ini mempunyai tarikh yang tidak sah."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Sijil ini tidak sah."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Ralat sijil tidak diketahui."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Sijil ini bukan daripada pihak berkuasa yang dipercayai."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nama tapak tidak sepadan dengan nama pada sijil."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Sijil ini telah tamat tempoh."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Sijil ini belum sah."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Sijil ini mempunyai tarikh yang tidak sah."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Sijil ini tidak sah."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Ralat sijil tidak diketahui."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Amaran keselamatan"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Lihat sijil"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Alamat:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Maklumat halaman"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-my/strings.xml b/packages/CaptivePortalLogin/res/values-my/strings.xml
index 902834b..04a07a4 100644
--- a/packages/CaptivePortalLogin/res/values-my/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-my/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"သင်ချိတ်ဆက်ရန် ကြိုးစားနေသည့် ကွန်ရက်သည် လုံခြုံရေးပြဿနာ ရှိနေသည်။"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ဥပမာ၊ ဝင်ရောက်ရန် စာမျက်နှာသည် ပြသထားသည့် အဖွဲ့အစည်းနှင့် သက်ဆိုင်မှု မရှိနိုင်ပါ။"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ဘရောက်ဇာမှတစ်ဆင့် ဆက်လုပ်ရန်"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ဤအသိအမှတ်ပြုလက်မှတ်သည် ယုံကြည်ရသော စစ်ဆေးရေးအဖွဲ့ထံမှ မဟုတ်ပါ။"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ဝဘ်ဆိုက်အမည်သည် အသိအမှတ်ပြုလက်မှတ်ရှိ အမည်နှင့် မကိုက်ညီပါ။"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ဤအသိအမှတ်ပြုလက်မှတ် သက်တမ်းကုန်နေပါပြီ။"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ဤအသိအမှတ်ပြုလက်မှတ်သည် မမှန်ကန်သေးပါ။"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ဤအသိအမှတ်ပြုလက်မှတ်ရှိ ရက်စွဲ မမှန်ကန်ပါ။"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ဤအသိအမှတ်ပြုလက်မှတ် မမှန်ကန်ပါ။"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"အသိအမှတ်ပြုလက်မှတ်ဆိုင်ရာ အမျိုးအမည်မသိ အမှား။"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"လုံခြုံရေး သတိပေးချက်"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"အသိအမှတ်ပြုလက်မှတ်ကို ကြည့်ရန်"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"လိပ်စာ-"</string>
+ <string name="page_info" msgid="4416941086705172545">"စာမျက်နှာ အချက်အလက်"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-nb/strings.xml b/packages/CaptivePortalLogin/res/values-nb/strings.xml
index 29c23ed..29ab438 100644
--- a/packages/CaptivePortalLogin/res/values-nb/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-nb/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Nettverket du prøver å logge på, har sikkerhetsproblemer."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Det er for eksempel mulig at påloggingssiden kanskje ikke tilhører organisasjonen som vises."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Fortsett likevel via nettleseren"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Sertifikatet er utløpt."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Sertifikatet er ikke gyldig ennå."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dette sertifikatet har en ugyldig dato."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Dette sertifikatet er ugyldig."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Ukjent sertifikatfeil."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sikkerhetsadvarsel"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Vis sertifikatet"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresse:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Sideinformasjon"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ne/strings.xml b/packages/CaptivePortalLogin/res/values-ne/strings.xml
index 87a30c0..07d7231 100644
--- a/packages/CaptivePortalLogin/res/values-ne/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ne/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"तपाईँले सामेल हुन प्रयास गरिरहनु भएको नेटवर्कमा सुरक्षा मुद्दाहरू छन्।"</string>
<string name="ssl_error_example" msgid="647898534624078900">"उदाहरणका लागि, लग इन पृष्ठ देखाइएको संस्थाको नहुन सक्छ।"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"जे भए पनि ब्राउजर मार्फत जारी राख्नुहोस्"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"यो विश्वसनीय प्राधिकरणबाट उपलब्ध गराइएको प्रमाणपत्र होइन।"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"प्रमाणपत्रमा भएको नाम साइटमा भएको नामसँग मेल खाँदैन।"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"यो प्रमाणपत्रको म्याद समाप्त भयो।"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"यो प्रमाणपत्र अझै मान्य छैन।"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"यो प्रमाणपत्रमा कुनै अमान्य मिति छ।"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"यो प्रमाणपत्र अमान्य छ।"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"प्रमाणपत्रसम्बन्धी अज्ञात त्रुटि।"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"सुरक्षासम्बन्धी चेतावनी"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"प्रमाणपत्र हेर्नुहोस्"</string>
+ <string name="ok" msgid="2817931639040794018">"ठिक छ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ठेगाना:"</string>
+ <string name="page_info" msgid="4416941086705172545">"पृष्ठको जानकारी"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-nl/strings.xml b/packages/CaptivePortalLogin/res/values-nl/strings.xml
index 2cbca06..a4df7b7 100644
--- a/packages/CaptivePortalLogin/res/values-nl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-nl/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Het netwerk waarmee u verbinding probeert te maken, heeft beveiligingsproblemen."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Zo hoort de weergegeven inlogpagina misschien niet bij de weergegeven organisatie."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Toch doorgaan via browser"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Pagina-informatie"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Beveiligingsmelding"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Certificaat weergeven"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dit is geen certificaat van een vertrouwde autoriteit."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"De naam van deze site komt niet overeen met de naam op het certificaat."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Dit certificaat is verlopen."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dit certificaat is nog niet geldig."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dit certificaat heeft een ongeldige datum."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Dit certificaat is ongeldig."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende certificaatfout."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Dit is geen certificaat van een vertrouwde autoriteit."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"De naam van deze site komt niet overeen met de naam op het certificaat."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Dit certificaat is verlopen."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Dit certificaat is nog niet geldig."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dit certificaat heeft een ongeldige datum."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Dit certificaat is ongeldig."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Onbekende certificaatfout."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Beveiligingswaarschuwing"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Certificaat weergeven"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Paginagegevens"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-or/strings.xml b/packages/CaptivePortalLogin/res/values-or/strings.xml
index 80074c3..f4c5dac 100644
--- a/packages/CaptivePortalLogin/res/values-or/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-or/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ଆପଣ ଯୋଗ ଦେବାକୁ ଚେଷ୍ଟା କରୁଥିବା ନେଟ୍ୱର୍କର ସୁରକ୍ଷା ସମସ୍ୟା ଅଛି।"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ଉଦାହରଣସ୍ୱରୂପ, ଲଗଇନ୍ ପୃଷ୍ଠା ଦେଖାଯାଇଥିବା ସଂସ୍ଥାର ନହୋଇଥାଇପାରେ।"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ବ୍ରାଉଜର୍ ଜରିଆରେ ଯେମିତିବି ହେଉ ଜାରି ରଖନ୍ତୁ"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ଏହି ସାର୍ଟିଫିକେଟ୍ ଏକ ବିଶ୍ବସ୍ତ କର୍ତ୍ତୃପକ୍ଷଙ୍କ ଠାରୁ ନୁହେଁ।"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ସାଇଟ୍ର ନାମ ସାର୍ଟିଫିକେଟ୍ର ନାମ ସହ ମିଶୁ ନାହିଁ।"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ଏହି ସାର୍ଟିଫିକେଟ୍ ସମୟସୀମା ଶେଷ ହୋଇଛି।"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ଏହି ସାର୍ଟିଫିକେଟ୍ ବର୍ତ୍ତମାନ ପର୍ଯ୍ୟନ୍ତ ବୈଧ ନୁହେଁ।"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ଏହି ସାର୍ଟିଫିକେଟ୍ରେ ଏକ ଅବୈଧ ତାରିଖ ରହିଛି।"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ଏହି ସାର୍ଟିଫିକେଟ୍ ବୈଧ ନୁହେଁ।"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"ଅଜଣା ସାର୍ଟିଫିକେଟ୍ ତ୍ରୁଟି।"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"ସୁରକ୍ଷା ଚେତାବନୀ"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ସାର୍ଟିଫିକେଟ୍ ଦେଖନ୍ତୁ"</string>
+ <string name="ok" msgid="2817931639040794018">"ଠିକ୍ ଅଛି"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ଠିକଣା:"</string>
+ <string name="page_info" msgid="4416941086705172545">"ପୃଷ୍ଠା ସୂଚନା"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pa/strings.xml b/packages/CaptivePortalLogin/res/values-pa/strings.xml
index 03e252f..b0d6187 100644
--- a/packages/CaptivePortalLogin/res/values-pa/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pa/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ਤੁਹਾਡੇ ਦੁਆਰਾ ਸ਼ਾਮਿਲ ਹੋਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੇ ਜਾ ਰਹੇ ਨੈੱਟਵਰਕ ਵਿੱਚ ਸੁਰੱਖਿਆ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ।"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ਉਦਾਹਰਣ ਵੱਜੋਂ, ਲੌਗ-ਇਨ ਪੰਨਾ ਦਿਖਾਈ ਗਈ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ।"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ਬ੍ਰਾਊਜ਼ਰ ਰਾਹੀਂ ਫਿਰ ਵੀ ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਕਿਸੇ ਭਰੋਸੇਯੋਗ ਅਥਾਰਿਟੀ ਦਾ ਨਹੀਂ ਹੈ।"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ਸਾਈਟ ਦਾ ਨਾਮ ਪ੍ਰਮਾਣ-ਪੱਤਰ \'ਤੇ ਦਿੱਤੇ ਨਾਮ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ ਹੈ।"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ਇਸ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਮਿਆਦ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ।"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਹਾਲੇ ਵੈਧ ਨਹੀਂ ਹੈ।"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ਇਸ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਤਾਰੀਖ ਅਵੈਧ ਹੈ।"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਵੈਧ ਹੈ।"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"ਅਗਿਆਤ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਗੜਬੜ।"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"ਸੁਰੱਖਿਆ ਚਿਤਾਵਨੀ"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੇਖੋ"</string>
+ <string name="ok" msgid="2817931639040794018">"ਠੀਕ ਹੈ"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ਪਤਾ:"</string>
+ <string name="page_info" msgid="4416941086705172545">"ਪੰਨੇ ਦੀ ਜਾਣਕਾਰੀ"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pl/strings.xml b/packages/CaptivePortalLogin/res/values-pl/strings.xml
index 9ba066e..178734d 100644
--- a/packages/CaptivePortalLogin/res/values-pl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pl/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"W sieci, z którą próbujesz się połączyć, występują problemy z zabezpieczeniami."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Na przykład strona logowania może nie należeć do wyświetlanej organizacji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Kontynuuj mimo to w przeglądarce"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Informacje o stronie"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ostrzeżenie zabezpieczeń"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Wyświetl certyfikat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certyfikat nie pochodzi od zaufanego urzędu."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nazwa witryny nie pasuje do nazwy na certyfikacie."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Ten certyfikat wygasł."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certyfikat nie jest jeszcze ważny."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Certyfikat ma nieprawidłową datę."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Certyfikat jest nieprawidłowy."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Nieznany błąd certyfikatu"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Certyfikat nie pochodzi od zaufanego urzędu."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nazwa witryny nie pasuje do nazwy na certyfikacie."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Certyfikat wygasł."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Certyfikat nie jest jeszcze ważny."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Certyfikat ma nieprawidłową datę."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Certyfikat jest nieprawidłowy."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Nieznany błąd certyfikatu."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Ostrzeżenie dotyczące bezpieczeństwa"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Wyświetl certyfikat"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informacje o stronie"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml
index 3d1064c..3aa82c4 100644
--- a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Esse certificado não é de uma autoridade confiável."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do site não corresponde ao nome no certificado."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Esse certificado expirou."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Esse certificado ainda não é válido."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"A data desse certificado é inválida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Esse certificado é inválido."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Erro de certificado desconhecido."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Aviso de segurança"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Endereço:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informações da página"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
index 5bef235..72d62a3 100644
--- a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual está a tentar aceder tem problemas de segurança."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim através do navegador"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Informações da página"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não pertence a uma autoridade fidedigna."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do Web site não corresponde ao nome constante no certificado."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro: certificado desconhecido."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado não pertence a uma autoridade fidedigna."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do site não corresponde ao nome constante no certificado."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado expirou."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado ainda não é válido."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado tem uma data inválida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado é inválido."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Erro de certificado desconhecido."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Aviso de segurança"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Endereço:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informações da página"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt/strings.xml b/packages/CaptivePortalLogin/res/values-pt/strings.xml
index ebe4148..3aa82c4 100644
--- a/packages/CaptivePortalLogin/res/values-pt/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pt/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Informações da página"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizar certificado"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não é de uma autoridade confiável."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do site não corresponde ao nome no certificado."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro de certificado desconhecido."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Esse certificado não é de uma autoridade confiável."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do site não corresponde ao nome no certificado."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Esse certificado expirou."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Esse certificado ainda não é válido."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"A data desse certificado é inválida."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Esse certificado é inválido."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Erro de certificado desconhecido."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Aviso de segurança"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Endereço:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informações da página"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ro/strings.xml b/packages/CaptivePortalLogin/res/values-ro/strings.xml
index e2e4eac..e3fc191 100644
--- a/packages/CaptivePortalLogin/res/values-ro/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ro/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Rețeaua la care încercați să vă conectați are probleme de securitate."</string>
<string name="ssl_error_example" msgid="647898534624078900">"De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Continuați oricum prin browser"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Informaţii pagină"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresă:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertisment de securitate"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vizualizaţi certificatul"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Acest certificat nu provine de la o autoritate de încredere."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Numele acestui site nu se potriveşte cu numele de pe certificat."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Acest certificat a expirat."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Acest certificat nu este încă valid."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Acest certificat are o dată nevalidă."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Acest certificat este nevalid."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Eroare de certificat necunoscută."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Acest certificat nu provine de la o autoritate de încredere."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Numele acestui site nu se potrivește cu numele de pe certificat."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Acest certificat a expirat."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Acest certificat nu este încă valid."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Acest certificat are o dată nevalidă."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Acest certificat este nevalid."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Eroare de certificat necunoscută."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avertisment de securitate"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Vizualizați certificatul"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresă:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informații despre pagină"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ru/strings.xml b/packages/CaptivePortalLogin/res/values-ru/strings.xml
index c0153e6..a976f57 100644
--- a/packages/CaptivePortalLogin/res/values-ru/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ru/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Сеть, к которой вы хотите подключиться, небезопасна."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Например, страница входа в аккаунт может быть фиктивной."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Игнорировать и открыть браузер"</string>
- <string name="ok" msgid="1509280796718850364">"ОК"</string>
- <string name="page_info" msgid="4048529256302257195">"Информация о странице"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Угроза безопасности"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Просмотреть сертификат"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Этот сертификат получен из ненадежных источников."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Название сайта не соответствует названию в сертификате."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Срок действия сертификата истек."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификат еще не действителен."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Дата этого сертификата недействительна."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Этот сертификат недействителен."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестная ошибка сертификата."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Этот сертификат получен из ненадежных источников."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Название сайта отличается от указанного в сертификате."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Срок действия сертификата истек."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Сертификат ещё не действителен."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Дата сертификата недействительна."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Сертификат недействителен."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Неизвестная ошибка, связанная с сертификатом."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Предупреждение об опасности"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Показать сертификат"</string>
+ <string name="ok" msgid="2817931639040794018">"ОК"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Адрес:"</string>
+ <string name="page_info" msgid="4416941086705172545">"О странице"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-si/strings.xml b/packages/CaptivePortalLogin/res/values-si/strings.xml
index a307913..edfe716 100644
--- a/packages/CaptivePortalLogin/res/values-si/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-si/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"ඔබ සම්බන්ධ වීමට උත්සහ කරන ජාලයේ ආරක්ෂක ගැටළු ඇත."</string>
<string name="ssl_error_example" msgid="647898534624078900">"උදාහරණයක් ලෙස, පුරනය වන පිටුව පෙන්වා ඇති සංවිධානයට අයිති නැති විය හැක."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"කෙසේ වුවත් බ්රවුසරය හරහා ඉදිරියට යන්න"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"මෙම සහතිකය විශ්වාසී අධිකාරියකින් නොවේ."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"අඩවියේ නම සහතිකයේ නමට නොගැළපේ."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"මෙම සහතිකය කල් ඉකුත් වී ඇත."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"මෙම සහතිකය තවම වලංගු නැත."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"මෙම සහතිකයට වලංගු නොවන දිනයක් ඇත."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"මෙම සහතිකය වලංගු නොවේ."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"නොදන්නා සහතික දෝෂයකි."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"ආරක්ෂක අවවාදයයි"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"සහතිකය බලන්න"</string>
+ <string name="ok" msgid="2817931639040794018">"හරි"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ලිපිනය:"</string>
+ <string name="page_info" msgid="4416941086705172545">"පිටු තොරතුරු"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sk/strings.xml b/packages/CaptivePortalLogin/res/values-sk/strings.xml
index 8ba24b1..c1100b8 100644
--- a/packages/CaptivePortalLogin/res/values-sk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sk/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Sieť, ku ktorej sa pokúšate pripojiť, má problémy so zabezpečením"</string>
<string name="ssl_error_example" msgid="647898534624078900">"Napríklad prihlasovacia stránka nemusí patriť uvedenej organizácii."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Pokračovať pomocou prehliadača"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Informácie o stránke"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornenie zabezpečenia"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobraziť certifikát"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochádza od dôveryhodnej autority."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Názov stránky sa nezhoduje s názvom uvedeným v certifikáte."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Platnosť certifikátu skončila."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát zatiaľ nie je platný."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tento certifikát má neplatný dátum."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznáma chyba certifikátu."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Tento certifikát nepochádza od dôveryhodnej autority."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Názov stránky sa nezhoduje s názvom uvedeným v certifikáte."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Platnosť certifikátu skončila."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Tento certifikát zatiaľ nie je platný."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Tento certifikát má neplatný dátum."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Tento certifikát je neplatný."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Neznáma chyba certifikátu."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Bezpečnostné upozornenie"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Zobraziť certifikát"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informácie o stránke"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sl/strings.xml b/packages/CaptivePortalLogin/res/values-sl/strings.xml
index b7d9a8a..b254240 100644
--- a/packages/CaptivePortalLogin/res/values-sl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sl/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Omrežje, ki se mu poskušate pridružiti, ima varnostne težave."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Stran za prijavo na primer morda ne pripada prikazani organizaciji."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Vseeno nadaljuj v brskalniku"</string>
- <string name="ok" msgid="1509280796718850364">"V redu"</string>
- <string name="page_info" msgid="4048529256302257195">"Podatki o strani"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Naslov:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Varnostno opozorilo"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži potrdilo"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Potrdila ni izdal zaupanja vreden overitelj."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ime spletnega mesta se ne ujema z imenom na potrdilu."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Potrdilo je poteklo."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"To potrdilo še ni veljavno."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Potrdilo ima neveljaven datum."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"To potrdilo ni veljavno."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznana napaka potrdila."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Potrdila ni izdal zaupanja vreden overitelj."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Ime spletnega mesta se ne ujema z imenom na potrdilu."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Potrdilo je poteklo."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"To potrdilo še ni veljavno."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"To potrdilo ima neveljaven datum."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"To potrdilo ni veljavno."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Neznana napaka potrdila."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Varnostno opozorilo"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Prikaži potrdilo"</string>
+ <string name="ok" msgid="2817931639040794018">"V redu"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Naslov:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Podatki o strani"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sq/strings.xml b/packages/CaptivePortalLogin/res/values-sq/strings.xml
index b06da6d..a178c40 100644
--- a/packages/CaptivePortalLogin/res/values-sq/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sq/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Rrjeti në të cilin po përpiqesh të bashkohesh ka probleme sigurie."</string>
<string name="ssl_error_example" msgid="647898534624078900">"për shembull, faqja e identifikimit mund të mos i përkasë organizatës së shfaqur."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Vazhdo gjithsesi nëpërmjet shfletuesit"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Kjo certifikatë nuk është nga një autoritet i besuar."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Emri i sajtit nuk përputhet me emrin në certifikatë."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Kjo certifikatë ka skaduar."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Kjo certifikatë nuk është ende e vlefshme."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Kjo certifikatë ka një datë të pavlefshme."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Kjo certifikatë është e pavlefshme."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Gabim i panjohur i certifikatës."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Paralajmërim për sigurinë"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Shiko certifikatën"</string>
+ <string name="ok" msgid="2817931639040794018">"Në rregull"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Informacionet e faqes"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sr/strings.xml b/packages/CaptivePortalLogin/res/values-sr/strings.xml
index 967c8ba..6b08369 100644
--- a/packages/CaptivePortalLogin/res/values-sr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sr/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Мрежа којој покушавате да се придружите има безбедносних проблема."</string>
<string name="ssl_error_example" msgid="647898534624078900">"На пример, страница за пријављивање можда не припада приказаној организацији."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Ипак настави преко прегледача"</string>
- <string name="ok" msgid="1509280796718850364">"Потврди"</string>
- <string name="page_info" msgid="4048529256302257195">"Информације о страници"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Безбедносно упозорење"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Прикажи сертификат"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Овај сертификат не потиче од поузданог ауторитета."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назив сајта се не подудара са називом на сертификату."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Овај сертификат је истекао."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Овај сертификат још увек није важећи."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Датум овог сертификата је неважећи."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Овај сертификат је неважећи."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Непозната грешка сертификата."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Овај сертификат не потиче из поузданог извора."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Назив сајта се не подудара са називом на сертификату."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Овај сертификат је истекао."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Овај сертификат још увек није важећи."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Датум овог сертификата је неважећи."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Овај сертификат је неважећи."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Непозната грешка сертификата."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Безбедносно упозорење"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Прикажи сертификат"</string>
+ <string name="ok" msgid="2817931639040794018">"Потврди"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Адреса:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Информације о страници"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sv/strings.xml b/packages/CaptivePortalLogin/res/values-sv/strings.xml
index 75356f0..7fe5432 100644
--- a/packages/CaptivePortalLogin/res/values-sv/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sv/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Nätverket du försöker ansluta till har säkerhetsproblem."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Det kan t.ex. hända att inloggningssidan inte tillhör den organisation som visas."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Fortsätt ändå via webbläsaren"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Sidinformation"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adress:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Säkerhetsvarning"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visa certifikat"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certifikatet kommer inte från en betrodd utfärdare."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Webbplatsens namn stämmer inte med namnet på certifikatet."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Certifikatet har upphört att gälla."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certifikatet är inte giltigt än."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Det här certifikatet har ett ogiltigt datum."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Certifikatet är ogiltigt."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Okänt certifikatfel."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Certifikatet kommer inte från en betrodd utfärdare."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Webbplatsens namn matchar inte namnet på certifikatet."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Det här certifikatet har löpt ut."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Det här certifikatet är inte giltigt än."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Datumet för det här certifikatet är ogiltigt."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Det här certifikatet är ogiltigt."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Okänt certifikatfel."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Säkerhetsvarning"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Visa certifikat"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adress:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Sidinformation"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sw/strings.xml b/packages/CaptivePortalLogin/res/values-sw/strings.xml
index feb2dde..297b72d 100644
--- a/packages/CaptivePortalLogin/res/values-sw/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sw/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mtandao unaojaribu kujiunga nao una matatizo ya usalama."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Kwa mfano, ukurasa wa kuingia katika akaunti unaweza usiwe unamilikiwa na shirika lililoonyeshwa."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Endelea hata hivyo kupitia kivinjari"</string>
- <string name="ok" msgid="1509280796718850364">"Sawa"</string>
- <string name="page_info" msgid="4048529256302257195">"Maelezo ya ukurasa"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Anwani:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ilani ya usalama"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tazama cheti"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Cheti hiki hakijatoka kwa mamlaka inayoaminika."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Jina la tovuti halilingani na jina lililo katika cheti."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Cheti hiki kimepitwa na muda"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Cheti bado si halali."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Cheti hiki kina tarehe batili."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Hati hii ni batili."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Hitilafu isiyojulikana ya cheti."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Cheti hiki hakijatoka kwa mamlaka inayoaminika."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Jina la tovuti halilingani na jina lililo katika cheti."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Muda wa kutumia cheti hiki umeisha."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Cheti hiki bado si halali."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Tarehe ya cheti hiki si sahihi."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Cheti hiki si sahihi."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Hitilafu isiyojulikana ya cheti."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Ilani ya usalama"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Angalia cheti"</string>
+ <string name="ok" msgid="2817931639040794018">"Sawa"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Anwani:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Maelezo ya ukurasa"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ta/strings.xml b/packages/CaptivePortalLogin/res/values-ta/strings.xml
index 6a60ed7..4517d15 100644
--- a/packages/CaptivePortalLogin/res/values-ta/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ta/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"நீங்கள் சேர முயற்சிக்கும் நெட்வொர்க்கில் பாதுகாப்புச் சிக்கல்கள் உள்ளன."</string>
<string name="ssl_error_example" msgid="647898534624078900">"எடுத்துக்காட்டாக, உள்நுழைவுப் பக்கமானது காட்டப்படும் அமைப்பிற்குச் சொந்தமானதாக இல்லாமல் இருக்கலாம்."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"பரவாயில்லை, உலாவி வழியாகத் தொடரவும்"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"இந்தச் சான்றிதழ் நம்பகமான நிறுவனத்தால் அங்கீகரிக்கப்படவில்லை."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"சான்றிதழிலுள்ள பெயருடன் வலைதளத்தின் பெயர் பொருந்தவில்லை."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"இந்தச் சான்றிதழ் காலாவதியாகிவிட்டது."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"இந்தச் சான்றிதழ் இன்னும் செல்லாததாக உள்ளது."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"இந்தச் சான்றிதழில் தவறான தேதி உள்ளது."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"இந்தச் சான்றிதழ் செல்லாதது."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"சான்றிதழில் அறியப்படாத பிழை உள்ளது."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"பாதுகாப்பு எச்சரிக்கை"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"சான்றிதழைக் காட்டு"</string>
+ <string name="ok" msgid="2817931639040794018">"சரி"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"முகவரி:"</string>
+ <string name="page_info" msgid="4416941086705172545">"பக்கத் தகவல்"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-te/strings.xml b/packages/CaptivePortalLogin/res/values-te/strings.xml
index c209d34..9bb4c11 100644
--- a/packages/CaptivePortalLogin/res/values-te/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-te/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"మీరు చేరడానికి ప్రయత్నిస్తున్న నెట్వర్క్ భద్రతా సమస్యలను కలిగి ఉంది."</string>
<string name="ssl_error_example" msgid="647898534624078900">"ఉదాహరణకు, లాగిన్ పేజీ చూపిన సంస్థకు చెందినది కాకపోవచ్చు."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించు"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ఈ సర్టిఫికెట్ విశ్వసనీయ అధికార సంస్థ నుండి కాదు."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"సైట్ యొక్క పేరు సర్టిఫికెట్లోని పేరుతో సరిపోలలేదు."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ఈ సర్టిఫికెట్ గడువు ముగిసింది."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ఈ సర్టిఫికెట్ ఇప్పటికీ చెల్లదు."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"ఈ సర్టిఫికెట్ చెల్లని తేదీని కలిగి ఉంది."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ఈ సర్టిఫికెట్ చెల్లుబాటు కాదు."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"తెలియని సర్టిఫికెట్ ఎర్రర్."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"భద్రతా హెచ్చరిక"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"సర్టిఫికెట్ని చూడండి"</string>
+ <string name="ok" msgid="2817931639040794018">"సరే"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"చిరునామా:"</string>
+ <string name="page_info" msgid="4416941086705172545">"పేజీ సమాచారం"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-th/strings.xml b/packages/CaptivePortalLogin/res/values-th/strings.xml
index 11a2131..739b505 100644
--- a/packages/CaptivePortalLogin/res/values-th/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-th/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"เครือข่ายที่คุณพยายามเข้าร่วมมีปัญหาด้านความปลอดภัย"</string>
<string name="ssl_error_example" msgid="647898534624078900">"ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"ดำเนินการต่อผ่านเบราว์เซอร์"</string>
- <string name="ok" msgid="1509280796718850364">"ตกลง"</string>
- <string name="page_info" msgid="4048529256302257195">"ข้อมูลหน้าเว็บ"</string>
- <string name="page_info_address" msgid="2222306609532903254">"ที่อยู่:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"คำเตือนเกี่ยวกับความปลอดภัย"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ดูใบรับรอง"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"ใบรับรองนี้ไม่ได้มาจากผู้ออกที่เชื่อถือได้"</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"ชื่อไซต์ไม่ตรงกับในใบรับรอง"</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"ใบรับรองนี้หมดอายุแล้ว"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ใบรับรองนี้ยังใช้งานไม่ได้"</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ใบรับรองนี้มีวันที่ไม่ถูกต้อง"</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"ใบรับรองนี้ไม่ถูกต้อง"</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"ข้อผิดพลาดใบรับรองที่ไม่รู้จัก"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"ใบรับรองนี้ไม่ได้มาจากผู้ออกที่เชื่อถือได้"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"ชื่อเว็บไซต์ไม่ตรงกับในใบรับรอง"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"ใบรับรองนี้หมดอายุแล้ว"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ใบรับรองนี้ยังใช้งานไม่ได้"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"วันที่ในใบรับรองนี้ไม่ถูกต้อง"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"ใบรับรองนี้ไม่ถูกต้อง"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"ข้อผิดพลาดใบรับรองที่ไม่รู้จัก"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"การแจ้งเตือนเกี่ยวกับความปลอดภัย"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ดูใบรับรอง"</string>
+ <string name="ok" msgid="2817931639040794018">"ตกลง"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"ที่อยู่:"</string>
+ <string name="page_info" msgid="4416941086705172545">"ข้อมูลหน้าเว็บ"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-tl/strings.xml b/packages/CaptivePortalLogin/res/values-tl/strings.xml
index 07a2479..483a589 100644
--- a/packages/CaptivePortalLogin/res/values-tl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-tl/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"May mga isyu sa seguridad ang network kung saan mo sinusubukang sumali."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Halimbawa, maaaring hindi sa organisasyong ipinapakita ang page sa pag-log in."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Magpatuloy pa rin sa pamamagitan ng browser"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Impormasyon ng pahina"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Babala sa seguridad"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tingnan ang certificate"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ang certificate ay hindi mula sa isang pinagkakatiwalaang kinauukulan."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ang pangalan ng site ay hindi tumutugma sa pangalan sa certificate."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Nag-expire na ang certificate na ito."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Wala pang bisa ang certificate na ito."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ang certificate ay mayroong di-wastong petsa."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Di-wasto ang certificate na ito."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Hindi kilalang error ng certificate."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ang certificate na ito ay mula sa hindi pinagkakatiwalaang awtoridad."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Ang pangalan ng site ay hindi tumutugma sa pangalan sa certificate."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Nag-expire na ang certificate na ito."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Hindi pa valid ang certificate na ito."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"May invalid na petsa ang certificate na ito."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Invalid ang certificate na ito."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Hindi kilalang error sa certificate."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Babala sa seguridad"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Tingnan ang certificate"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Impormasyon ng page"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-tr/strings.xml b/packages/CaptivePortalLogin/res/values-tr/strings.xml
index cdedd33..f51cfbd 100644
--- a/packages/CaptivePortalLogin/res/values-tr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-tr/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Katılmaya çalıştığınız ağda güvenlik sorunları var."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Örneğin, giriş sayfası, gösterilen kuruluşa ait olmayabilir."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Yine de tarayıcıyla devam et"</string>
- <string name="ok" msgid="1509280796718850364">"Tamam"</string>
- <string name="page_info" msgid="4048529256302257195">"Sayfa bilgileri"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Güvenlik uyarısı"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Sertifikayı görüntüle"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Bu sertifika güvenilir bir yetkiliden değil."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sitenin adı sertifika üzerindeki adla eşleşmiyor."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Bu sertifikanın süresi dolmuş."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Bu sertifika henüz geçerli değil."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Bu sertifikanın tarihi geçersiz."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Bu sertifika geçersiz."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Bilinmeyen sertifika hatası."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Bu sertifika güvenilir bir yetkiliden değil."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Sitenin adı sertifika üzerindeki adla eşleşmiyor."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Bu sertifikanın süresi dolmuş."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Bu sertifika henüz geçerli değil."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Bu sertifikanın tarihi geçersiz."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Bu sertifika geçersiz."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Bilinmeyen sertifika hatası."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Güvenlik uyarısı"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Sertifikayı göster"</string>
+ <string name="ok" msgid="2817931639040794018">"Tamam"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Sayfa bilgileri"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-uk/strings.xml b/packages/CaptivePortalLogin/res/values-uk/strings.xml
index 0f4cd16..c1a5d05 100644
--- a/packages/CaptivePortalLogin/res/values-uk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-uk/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"У мережі, до якої ви намагаєтеся під’єднатись, є проблеми з безпекою."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Наприклад, сторінка входу може не належати вказаній організації."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Усе одно продовжити у веб-переглядачі"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Інфо про стор."</string>
- <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Застереж. про небезп."</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Переглянути сертиф."</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертифікат видано ненадійним центром сертифікації."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назва сайту не збігається з назвою в сертифікаті."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Термін дії сертиф. завершився."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Цей сертифікат ще не дійсний."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Цей сертифікат має недійсну дату."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Цей сертифікат недійсний."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Помилка невідомого сертифіката."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Сертифікат видано ненадійним центром сертифікації."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Назва сайту не збігається з назвою в сертифікаті."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Термін дії сертифіката закінчився."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Цей сертифікат ще не дійсний."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Цей сертифікат має недійсну дату."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Цей сертифікат недійсний."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Помилка невідомого сертифіката."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Застереження про небезпеку"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Переглянути сертифікат"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Адреса:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Інформація про сторінку"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ur/strings.xml b/packages/CaptivePortalLogin/res/values-ur/strings.xml
index 05d8fb9..e6f8539 100644
--- a/packages/CaptivePortalLogin/res/values-ur/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ur/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"جس نیٹ ورک میں آپ شامل ہونے کی کوشش کر رہے ہیں اس میں سیکیورٹی کے مسائل ہیں۔"</string>
<string name="ssl_error_example" msgid="647898534624078900">"مثال کے طور پر ہو سکتا ہے کہ لاگ ان صفحہ دکھائی گئی تنظیم سے تعلق نہ رکھتا ہو۔"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"براؤزر کے ذریعے بہرحال جاری رکھیں"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"یہ سرٹیفکیٹ قابل اعتماد اتھارٹی سے حاصل شدہ نہیں ہے۔"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"سائٹ کا نام سرٹیفکیٹ پر موجود نام سے مماثل نہیں ہے۔"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"اس سرٹیفکیٹ کی میعاد ختم ہو گئی ہے۔"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"یہ سرٹیفکیٹ ابھی تک درست نہیں ہے۔"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"اس سرٹیفکیٹ میں ایک غلط تاریخ ہے۔"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"یہ سرٹیفیکیٹ غلط ہے۔"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"سرٹیفکیٹ کی نامعلوم خرابی۔"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"سیکیورٹی وارننگ"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"سرٹیفکیٹ دیکھیں"</string>
+ <string name="ok" msgid="2817931639040794018">"ٹھیک ہے"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"پتہ:"</string>
+ <string name="page_info" msgid="4416941086705172545">"صفحہ کی معلومات"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-uz/strings.xml b/packages/CaptivePortalLogin/res/values-uz/strings.xml
index cac96ea..4009db6a 100644
--- a/packages/CaptivePortalLogin/res/values-uz/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-uz/strings.xml
@@ -9,4 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Siz ulanmoqchi bo‘lgan tarmoqda xavfsizlik bilan bog‘liq muammolar mavjud."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Masalan, tizimga kirish sahifasi ko‘rsatilgan tashkilotga tegishli bo‘lmasligi mumkin."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"E’tiborsiz qoldirilsin va brauzer ochilsin"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Bu sertifikat ishonchsiz manbadan olingan."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Sayt nomi sertifikatdagi nomga mos emas."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Bu sertifikat muddati tugagan."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Bu sertifikat hali yaroqli emas."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Bu sertifikatdagi sana xato."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Bu sertifikat yaroqsiz."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Sertifikatga aloqador notanish xato."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Xavfsizlikka oid ogohlantirish"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Sertifikatni ochish"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Manzil:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Sahifa haqida"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-vi/strings.xml b/packages/CaptivePortalLogin/res/values-vi/strings.xml
index 9c702b9..bd4e96a 100644
--- a/packages/CaptivePortalLogin/res/values-vi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-vi/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Mạng mà bạn đang cố gắng tham gia có vấn đề về bảo mật."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Ví dụ, trang đăng nhập có thể không thuộc về tổ chức được hiển thị."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Vẫn tiếp tục qua trình duyệt"</string>
- <string name="ok" msgid="1509280796718850364">"OK"</string>
- <string name="page_info" msgid="4048529256302257195">"Thông tin trang"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Địa chỉ:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Cảnh báo bảo mật"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Xem chứng chỉ"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Chứng chỉ này không xuất phát từ tổ chức phát hành đáng tin cậy."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Tên của trang web không khớp với tên trên chứng chỉ."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Chứng chỉ này đã hết hạn."</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Chứng chỉ này chưa hợp lệ."</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Chứng chỉ này có ngày không hợp lệ."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Chứng chỉ này không hợp lệ."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Lỗi chứng chỉ không xác định."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Chứng chỉ này không xuất phát từ tổ chức phát hành đáng tin cậy."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Tên của trang web không khớp với tên trên chứng chỉ."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Chứng chỉ này đã hết hạn."</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Chứng chỉ này chưa hợp lệ."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Chứng chỉ này có ngày không hợp lệ."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Chứng chỉ này không hợp lệ."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Lỗi chứng chỉ không xác định."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Cảnh báo bảo mật"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Xem chứng chỉ"</string>
+ <string name="ok" msgid="2817931639040794018">"OK"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Địa chỉ:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Thông tin trang"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
index 70c2a08..5beaa2e 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"您尝试加入的网络存在安全问题。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"例如,登录页面可能并不属于页面上显示的单位。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"仍然通过浏览器继续操作"</string>
- <string name="ok" msgid="1509280796718850364">"确定"</string>
- <string name="page_info" msgid="4048529256302257195">"网页信息"</string>
- <string name="page_info_address" msgid="2222306609532903254">"网址:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全警告"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看证书"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"该证书并非来自可信的授权中心。"</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"网站的名称与证书上的名称不一致。"</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"该证书已过期。"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"该证书尚未生效。"</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"该证书的日期无效。"</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"该证书无效。"</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"未知证书错误。"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"该证书并非来自可信的授权中心。"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"网站的名称与证书上的名称不一致。"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"该证书已过期。"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"该证书尚未生效。"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"该证书的日期无效。"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"该证书无效。"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"未知证书错误。"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"安全警告"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"查看证书"</string>
+ <string name="ok" msgid="2817931639040794018">"确定"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"地址:"</string>
+ <string name="page_info" msgid="4416941086705172545">"页面信息"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
index df1c700..7987023 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"您正在嘗試加入的網絡有安全性問題。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"例如,登入頁面並不屬於所顯示的機構。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
- <string name="ok" msgid="1509280796718850364">"確定"</string>
- <string name="page_info" msgid="4048529256302257195">"網頁資訊"</string>
- <string name="page_info_address" msgid="2222306609532903254">"地址:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看憑證"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非由受信任的權威機構發出。"</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"這個憑證已過期。"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"此憑證的日期無效。"</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"此憑證是無效的。"</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"此憑證並非由信任的機構發出。"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"網站名稱與憑證上的名稱不符。"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"此憑證已過期。"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"此憑證尚未生效。"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"此憑證的日期無效。"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"此憑證無效。"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"不明的憑證錯誤。"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"安全性警告"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"查看憑證"</string>
+ <string name="ok" msgid="2817931639040794018">"確定"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"電郵地址:"</string>
+ <string name="page_info" msgid="4416941086705172545">"頁面資料"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
index 2a2e397..9f92f39 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"你嘗試加入的網路有安全問題。"</string>
<string name="ssl_error_example" msgid="647898534624078900">"例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
- <string name="ok" msgid="1509280796718850364">"確定"</string>
- <string name="page_info" msgid="4048529256302257195">"頁面資訊"</string>
- <string name="page_info_address" msgid="2222306609532903254">"位址:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"檢視憑證"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非來自信任的授權單位。"</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"此憑證已過期"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"這個憑證的日期無效。"</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"這個憑證無效。"</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"這個憑證並非來自信任的授權單位。"</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"網站名稱與憑證上的名稱不相符。"</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"這個憑證已過期。"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"這個憑證尚未生效。"</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"這個憑證的日期無效。"</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"這個憑證無效。"</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"不明的憑證錯誤。"</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"安全性警告"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"查看憑證"</string>
+ <string name="ok" msgid="2817931639040794018">"確定"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"地址:"</string>
+ <string name="page_info" msgid="4416941086705172545">"頁面資訊"</string>
</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zu/strings.xml b/packages/CaptivePortalLogin/res/values-zu/strings.xml
index 7943645..ebb0f54 100644
--- a/packages/CaptivePortalLogin/res/values-zu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zu/strings.xml
@@ -9,16 +9,16 @@
<string name="ssl_error_warning" msgid="6653188881418638872">"Inethiwekhi ozama ukuyijoyina inezinkinga zokuvikela."</string>
<string name="ssl_error_example" msgid="647898534624078900">"Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Qhubeka noma kunjalo ngesiphequluli"</string>
- <string name="ok" msgid="1509280796718850364">"KULUNGILE"</string>
- <string name="page_info" msgid="4048529256302257195">"Ulwazi lekhasi"</string>
- <string name="page_info_address" msgid="2222306609532903254">"Ikheli:"</string>
- <string name="ssl_security_warning_title" msgid="6607795404322797541">"Isexwayiso sokuvikeleka"</string>
- <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Buka isitifiketi"</string>
- <string name="ssl_error_untrusted" msgid="7754507359360636447">"Lesi sitifiketi asiphumi embusweni othembekile."</string>
- <string name="ssl_error_mismatch" msgid="3809794439740523641">"Igama lale ngosi alifani negama elikusitifiketi."</string>
- <string name="ssl_error_expired" msgid="5739349389499575559">"Lesi sitifiketi siphelelwe yisikhathi"</string>
- <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Lesi sitifiketi asilungile okwamanje"</string>
- <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Lesi sitifiketi sinosuku olungalungile."</string>
- <string name="ssl_error_invalid" msgid="9041704741505449967">"Lesi sitifiketi asilungile."</string>
- <string name="ssl_error_unknown" msgid="5679243486524754571">"Iphutha lesitifiketi elingaziwa."</string>
+ <string name="ssl_error_untrusted" msgid="1496280318271264520">"Lesi sitifiketi asisuki kusiphathimandla esithembekile."</string>
+ <string name="ssl_error_mismatch" msgid="3060364165934822383">"Igama lesayithi alifani negama elikusitifiketi."</string>
+ <string name="ssl_error_expired" msgid="1501588340716182495">"Lesi sitifiketi siphelelwe yisikhathi"</string>
+ <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Lesi sitifiketi asivumelekile okwamanje."</string>
+ <string name="ssl_error_date_invalid" msgid="88425990680059223">"Lesi sitifiketi sinosuku olungalungile."</string>
+ <string name="ssl_error_invalid" msgid="2540546515565633432">"Lesi sitifiketi asilungile."</string>
+ <string name="ssl_error_unknown" msgid="4405203446079465859">"Iphutha lesitifiketi elingaziwa."</string>
+ <string name="ssl_security_warning_title" msgid="8768539813847504404">"Isexwayiso sokuvikeleka"</string>
+ <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Buka isitifiketi"</string>
+ <string name="ok" msgid="2817931639040794018">"KULUNGILE"</string>
+ <string name="page_info_address" msgid="1261481517455692363">"Ikheli:"</string>
+ <string name="page_info" msgid="4416941086705172545">"Ulwazi lekhasi"</string>
</resources>
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_out.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_out.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
index ed637a7..6f12338 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
index 227c981..9f8c12e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
index 5901ff4..adc1f72 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
index 41cbe4b..dec5c05 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
index 341e7e0..986a9cb 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
index 6ae7413..80b38b3 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
index 06ac9e3..721376c 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
index 4baefb8..246099e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
index 2d0deb9..9a1c642 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
similarity index 95%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
index 3315220..1414b66 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/drawable/car_add_circle_round.xml b/packages/CarSystemUI/res/drawable/car_add_circle_round.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_add_circle_round.xml
rename to packages/CarSystemUI/res/drawable/car_add_circle_round.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_add_white.xml b/packages/CarSystemUI/res/drawable/car_ic_add_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_add_white.xml
rename to packages/CarSystemUI/res/drawable/car_ic_add_white.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
similarity index 94%
rename from packages/SystemUI/res/drawable/car_ic_arrow.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow.xml
index d400ed8..cfacbf9 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
@@ -1,5 +1,5 @@
<!--
- ~ Copyright (C) 2017 The Android Open Source Project
+ ~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
similarity index 93%
rename from packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
index 33a512e..81e7262 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
@@ -1,5 +1,5 @@
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_hvac.xml
rename to packages/CarSystemUI/res/drawable/car_ic_hvac.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml b/packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
rename to packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
diff --git a/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
new file mode 100644
index 0000000..eb501e5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners
+ android:bottomLeftRadius="@dimen/car_radius_3"
+ android:topLeftRadius="0dp"
+ android:bottomRightRadius="@dimen/car_radius_3"
+ android:topRightRadius="0dp"
+ />
+</shape>
diff --git a/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
new file mode 100644
index 0000000..34578fe
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="18.0"
+ android:viewportHeight="18.0">
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M9.57,8.5l2.79,-2.78c0.3,-0.3 0.3,-0.8 0,-1.1L9.04,1.29L9.02,1.27C8.7,0.98 8.21,1 7.91,1.31C7.78,1.45 7.71,1.64 7.71,1.84v4.79L4.69,3.61c-0.3,-0.3 -0.79,-0.3 -1.09,0s-0.3,0.79 0,1.09L7.39,8.5L3.6,12.29c-0.3,0.3 -0.3,0.79 0,1.09s0.79,0.3 1.09,0l3.01,-3.01v4.8c0,0.42 0.35,0.77 0.77,0.77c0.19,0 0.39,-0.07 0.53,-0.21l0.04,-0.04l3.32,-3.32c0.3,-0.3 0.3,-0.8 0,-1.1L9.57,8.5zM9.19,6.77v-3.2l1.6,1.6L9.19,6.77zM9.19,13.42v-3.2l1.6,1.6L9.19,13.42zM4.03,9.29c-0.44,0.44 -1.15,0.44 -1.58,0C2.02,8.86 2.02,8.16 2.45,7.72l0.01,-0.01C2.89,7.27 3.59,7.27 4.02,7.7l0.01,0.01C4.47,8.15 4.47,8.85 4.03,9.29zM14.44,7.71c0.44,0.44 0.44,1.15 0,1.58c-0.44,0.44 -1.15,0.44 -1.58,0c-0.44,-0.43 -0.44,-1.13 -0.01,-1.57l0.01,-0.01C13.3,7.28 14,7.27 14.43,7.7C14.44,7.7 14.44,7.71 14.44,7.71z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
index f5a91b5..e1e389d 100644
--- a/packages/CarSystemUI/res/drawable/ic_mic_white.xml
+++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/CarSystemUI/res/layout/car_facet_button.xml
similarity index 97%
rename from packages/SystemUI/res/layout/car_facet_button.xml
rename to packages/CarSystemUI/res/layout/car_facet_button.xml
index ad86049..8e7ebad 100644
--- a/packages/SystemUI/res/layout/car_facet_button.xml
+++ b/packages/CarSystemUI/res/layout/car_facet_button.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2017, The Android Open Source Project
+** Copyright 2018, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index ee8d357..1d67286 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (C) 2015 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index c9f5148..6cd70d6 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2015 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_left_navigation_bar.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2016, The Android Open Source Project
+** Copyright 2018, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
similarity index 95%
rename from packages/SystemUI/res/layout/car_navigation_button.xml
rename to packages/CarSystemUI/res/layout/car_navigation_button.xml
index 4062eb8..6d8cca9 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2016, The Android Open Source Project
+** Copyright 2018, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_qs_footer.xml
rename to packages/CarSystemUI/res/layout/car_qs_footer.xml
index 3afd4ea..6f19cfc 100644
--- a/packages/SystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_qs_panel.xml
rename to packages/CarSystemUI/res/layout/car_qs_panel.xml
index e7413de..dfa48c3 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
similarity index 98%
copy from packages/SystemUI/res/layout/car_left_navigation_bar.xml
copy to packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2016, The Android Open Source Project
+** Copyright 2018, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/colors_car.xml
rename to packages/CarSystemUI/res/values/colors_car.xml
index 49bfb25..2f720f5 100644
--- a/packages/SystemUI/res/values/colors_car.xml
+++ b/packages/CarSystemUI/res/values/colors_car.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
- * Copyright 2017, The Android Open Source Project
+ * Copyright 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml
similarity index 97%
rename from packages/SystemUI/res/values/dimens_car.xml
rename to packages/CarSystemUI/res/values/dimens_car.xml
index afbe176..c027f81 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/CarSystemUI/res/values/dimens_car.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * Copyright (c) 2016, The Android Open Source Project
+ * Copyright (c) 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/values/ids_car.xml b/packages/CarSystemUI/res/values/ids_car.xml
similarity index 100%
rename from packages/SystemUI/res/values/ids_car.xml
rename to packages/CarSystemUI/res/values/ids_car.xml
diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/integers_car.xml
rename to packages/CarSystemUI/res/values/integers_car.xml
index fc3623c..472c957 100644
--- a/packages/SystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2017, The Android Open Source Project
+ Copyright (c) 2018, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml
similarity index 96%
rename from packages/SystemUI/res/values/strings_car.xml
rename to packages/CarSystemUI/res/values/strings_car.xml
index 2890cf2..83e91c5 100644
--- a/packages/SystemUI/res/values/strings_car.xml
+++ b/packages/CarSystemUI/res/values/strings_car.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/**
- * Copyright (c) 2016, The Android Open Source Project
+ * Copyright (c) 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index dfe5704..f57f26d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -28,6 +28,8 @@
import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.volume.CarVolumeDialogComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
/**
* Class factory to provide car specific SystemUI components.
@@ -39,6 +41,10 @@
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
}
+ public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+ return new CarVolumeDialogComponent(systemUi, context);
+ }
+
@Override
public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
rename to packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 50fefe9..0563418 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,8 +11,9 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.car;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 0389030..b74f199 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -1,16 +1,19 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
+
package com.android.systemui.qs.car;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
index 3e82c54..41c37d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
@@ -1,16 +1,19 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
+
package com.android.systemui.qs.car;
import android.animation.Animator;
@@ -223,7 +226,7 @@
private void animateHeightChange(boolean opening) {
// Animation in progress; cancel it to avoid contention.
- if (mAnimatorSet != null){
+ if (mAnimatorSet != null) {
mAnimatorSet.cancel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index 083a747..d5dd3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -1,16 +1,19 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
+
package com.android.systemui.qs.car;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index fc39648..58f80a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -58,28 +58,31 @@
private final Context mContext;
private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
- private BluetoothHeadsetClient mBluetoothHeadsetClient;
-
private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private BluetoothHeadsetClient mBluetoothHeadsetClient;
+ private final ServiceListener mHfpServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+ }
+ }
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = null;
+ }
+ }
+ };
private int mLevel;
-
- /**
- * An interface indicating the container of a View that will display what the information
- * in the {@link CarBatteryController}.
- */
- public interface BatteryViewHandler {
- void hideBatteryView();
- void showBatteryView();
- }
-
private BatteryViewHandler mBatteryViewHandler;
public CarBatteryController(Context context) {
mContext = context;
if (mAdapter == null) {
- return;
+ return;
}
mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -159,7 +162,7 @@
}
BluetoothDevice device =
- (BluetoothDevice)intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
+ (BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
updateBatteryIcon(device, newState);
}
}
@@ -261,20 +264,14 @@
}
}
- private final ServiceListener mHfpServiceListener = new ServiceListener() {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
- }
- }
+ /**
+ * An interface indicating the container of a View that will display what the information
+ * in the {@link CarBatteryController}.
+ */
+ public interface BatteryViewHandler {
+ void hideBatteryView();
- @Override
- public void onServiceDisconnected(int profile) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = null;
- }
- }
- };
+ void showBatteryView();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 20986ad..56db242 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.car;
import android.app.ActivityManager;
@@ -34,7 +50,6 @@
* Add facet button to this controller. The expected use is for the facet button
* to get a reference to this controller via {@link com.android.systemui.Dependency}
* and self add.
- * @param facetButton
*/
public void addFacetButton(CarFacetButton facetButton) {
String[] categories = facetButton.getCategories();
@@ -70,15 +85,16 @@
* They will then be compared with the supplied StackInfo list.
* The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
* for consideration if it has the same displayId as the CarFacetButtons.
- * @param taskInfo of the currently running application
+ *
+ * @param stackInfoList of the currently running application
*/
public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
int displayId = getDisplayId();
ActivityManager.StackInfo validStackInfo = null;
- for (ActivityManager.StackInfo stackInfo :stackInfoList) {
+ for (ActivityManager.StackInfo stackInfo : stackInfoList) {
// If the display id is unknown or it matches the stack, it's valid for use
- if ((displayId == -1 || displayId == stackInfo.displayId) &&
- stackInfo.topActivity != null) {
+ if ((displayId == -1 || displayId == stackInfo.displayId)
+ && stackInfo.topActivity != null) {
validStackInfo = stackInfo;
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 084c136..e640baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.car;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 4bff5ba..2d90f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,8 @@
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.StatusBarState;
@@ -252,6 +254,11 @@
addTemperatureViewToController(mStatusBarWindow);
}
+ @Override
+ protected QS createDefaultQSFragment() {
+ return new CarQSFragment();
+ }
+
private BatteryController createBatteryController() {
mCarBatteryController = new CarBatteryController(mContext);
mCarBatteryController.addBatteryViewHandler(this);
@@ -549,7 +556,7 @@
*/
public void dismissKeyguard() {
executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
- true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+ true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
index d0f0629..8c6b9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.car;
import android.content.Context;
@@ -16,13 +32,13 @@
ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils) {
super(context, callback, lockPatternUtils);
- mShouldHideNavBar =context.getResources()
+ mShouldHideNavBar = context.getResources()
.getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
}
@Override
protected void updateNavigationBarVisibility(boolean navBarVisible) {
- if(!mShouldHideNavBar) {
+ if (!mShouldHideNavBar) {
return;
}
CarStatusBar statusBar = (CarStatusBar) mStatusBar;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
index f2923f7..3288927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.car;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
@@ -29,7 +45,7 @@
*/
public class ConnectedDeviceSignalController extends BroadcastReceiver implements
BluetoothController.Callback {
- private final static String TAG = "DeviceSignalCtlr";
+ private static final String TAG = "DeviceSignalCtlr";
/**
* The value that indicates if a network is unavailable. This value is according ot the
@@ -70,6 +86,21 @@
private final SignalDrawable mSignalDrawable;
private BluetoothHeadsetClient mBluetoothHeadsetClient;
+ private final ServiceListener mHfpServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = null;
+ }
+ }
+ };
public ConnectedDeviceSignalController(Context context, View signalsView) {
mContext = context;
@@ -87,7 +118,7 @@
new ScalingDrawableWrapper(mSignalDrawable, mIconScaleFactor));
if (mAdapter == null) {
- return;
+ return;
}
mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -236,20 +267,4 @@
mSignalsView.setVisibility(View.GONE);
}
}
-
- private final ServiceListener mHfpServiceListener = new ServiceListener() {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
- }
- }
-
- @Override
- public void onServiceDisconnected(int profile) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = null;
- }
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 47941bf..730c3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -124,4 +124,4 @@
Log.d(TAG, message);
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 2ebf5eb..23fe594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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.systemui.statusbar.car;
@@ -44,7 +44,7 @@
// Initialize user grid.
mUserGridView = container.findViewById(R.id.user_grid);
GridLayoutManager layoutManager = new GridLayoutManager(context,
- context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+ context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
mUserGridView.getRecyclerView().setLayoutManager(layoutManager);
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(this::onUserSelected);
@@ -54,7 +54,7 @@
hide();
mShortAnimDuration = container.getResources()
- .getInteger(android.R.integer.config_shortAnimTime);
+ .getInteger(android.R.integer.config_shortAnimTime);
}
/**
@@ -108,4 +108,4 @@
});
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index d802ed8..fb2b57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -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.systemui.statusbar.car;
@@ -210,7 +210,7 @@
public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
UserRecord userRecord = mUsers.get(position);
RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
- getUserRecordIcon(userRecord));
+ getUserRecordIcon(userRecord));
circleIcon.setCircular(true);
holder.mUserAvatarImageView.setImageDrawable(circleIcon);
holder.mUserNameTextView.setText(userRecord.mInfo.name);
@@ -254,13 +254,13 @@
private void showMaxUserLimitReachedDialog() {
AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
- .setTitle(R.string.user_limit_reached_title)
- .setMessage(getResources().getQuantityString(
- R.plurals.user_limit_reached_message,
- mCarUserManagerHelper.getMaxSupportedRealUsers(),
- mCarUserManagerHelper.getMaxSupportedRealUsers()))
- .setPositiveButton(android.R.string.ok, null)
- .create();
+ .setTitle(R.string.user_limit_reached_title)
+ .setMessage(getResources().getQuantityString(
+ R.plurals.user_limit_reached_message,
+ mCarUserManagerHelper.getMaxSupportedRealUsers(),
+ mCarUserManagerHelper.getMaxSupportedRealUsers()))
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
// Sets window flags for the SysUI dialog
SystemUIDialog.applyFlags(maxUsersDialog);
maxUsersDialog.show();
@@ -268,17 +268,17 @@
private void showConfirmAddUserDialog() {
String message = mRes.getString(R.string.user_add_user_message_setup)
- .concat(System.getProperty("line.separator"))
- .concat(System.getProperty("line.separator"))
- .concat(mRes.getString(R.string.user_add_user_message_update));
+ .concat(System.getProperty("line.separator"))
+ .concat(System.getProperty("line.separator"))
+ .concat(mRes.getString(R.string.user_add_user_message_update));
AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
- .setTitle(R.string.user_add_user_title)
- .setMessage(message)
- .setNegativeButton(android.R.string.cancel, this)
- .setPositiveButton(android.R.string.ok, this)
- .setOnCancelListener(this)
- .create();
+ .setTitle(R.string.user_add_user_title)
+ .setMessage(message)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
// Sets window flags for the SysUI dialog
SystemUIDialog.applyFlags(addUserDialog);
addUserDialog.show();
@@ -298,7 +298,7 @@
if (userRecord.mIsAddUser) {
return UserIcons.convertToBitmap(mContext
- .getDrawable(R.drawable.car_add_circle_round));
+ .getDrawable(R.drawable.car_add_circle_round));
}
return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 6c924e3..aec31ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -41,117 +41,13 @@
public class HvacController {
public static final String TAG = "HvacController";
- public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
+ public static final int BIND_TO_HVAC_RETRY_DELAY = 5000;
private Context mContext;
private Handler mHandler;
private Car mCar;
private CarHvacManager mHvacManager;
private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
-
- public HvacController(Context context) {
- mContext = context;
- }
-
- /**
- * Create connection to the Car service. Note: call backs from the Car service
- * ({@link CarHvacManager}) will happen on the same thread this method was called from.
- */
- public void connectToCarService() {
- mHandler = new Handler();
- mCar = Car.createCar(mContext, mServiceConnection, mHandler);
- if (mCar != null) {
- // note: this connect call handles the retries
- mCar.connect();
- }
- }
-
- /**
- * Registers callbacks and initializes components upon connection.
- */
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- service.linkToDeath(mRestart, 0);
- mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
- mHvacManager.registerCallback(mHardwareCallback);
- initComponents();
- } catch (Exception e) {
- Log.e(TAG, "Failed to correctly connect to HVAC", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyHvacManager();
- }
- };
-
- private void destroyHvacManager() {
- if (mHvacManager != null) {
- mHvacManager.unregisterCallback(mHardwareCallback);
- mHvacManager = null;
- }
- }
-
- /**
- * If the connection to car service goes away then restart it.
- */
- private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- Log.d(TAG, "Death of HVAC triggering a restart");
- if (mCar != null) {
- mCar.disconnect();
- }
- destroyHvacManager();
- mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
- }
- };
-
- /**
- * Add component to list and initialize it if the connection is up.
- * @param temperatureView
- */
- public void addHvacTextView(TemperatureView temperatureView) {
-
- HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
- if (!mTempComponents.containsKey(hvacKey)) {
- mTempComponents.put(hvacKey, new ArrayList<>());
- }
- mTempComponents.get(hvacKey).add(temperatureView);
- initComponent(temperatureView);
- }
-
- private void initComponents() {
- Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
- mTempComponents.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
- List<TemperatureView> temperatureViews = next.getValue();
- for (TemperatureView view : temperatureViews) {
- initComponent(view);
- }
- }
- }
-
-
- private void initComponent(TemperatureView view) {
- int id = view.getPropertyId();
- int zone = view.getAreaId();
- try {
- if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
- view.setTemp(Float.NaN);
- return;
- }
- view.setTemp(mHvacManager.getFloatProperty(id, zone));
- } catch (Exception e) {
- view.setTemp(Float.NaN);
- Log.e(TAG, "Failed to get value from hvac service", e);
- }
- }
-
/**
* Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
* match.
@@ -179,10 +75,109 @@
@Override
public void onErrorEvent(final int propertyId, final int zone) {
- Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
- " zone: " + zone);
+ Log.d(TAG, "HVAC error event, propertyId: " + propertyId
+ + " zone: " + zone);
}
};
+ /**
+ * If the connection to car service goes away then restart it.
+ */
+ private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "Death of HVAC triggering a restart");
+ if (mCar != null) {
+ mCar.disconnect();
+ }
+ destroyHvacManager();
+ mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
+ }
+ };
+ /**
+ * Registers callbacks and initializes components upon connection.
+ */
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ service.linkToDeath(mRestart, 0);
+ mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+ mHvacManager.registerCallback(mHardwareCallback);
+ initComponents();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to correctly connect to HVAC", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ destroyHvacManager();
+ }
+ };
+
+ public HvacController(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Create connection to the Car service. Note: call backs from the Car service
+ * ({@link CarHvacManager}) will happen on the same thread this method was called from.
+ */
+ public void connectToCarService() {
+ mHandler = new Handler();
+ mCar = Car.createCar(mContext, mServiceConnection, mHandler);
+ if (mCar != null) {
+ // note: this connect call handles the retries
+ mCar.connect();
+ }
+ }
+
+ private void destroyHvacManager() {
+ if (mHvacManager != null) {
+ mHvacManager.unregisterCallback(mHardwareCallback);
+ mHvacManager = null;
+ }
+ }
+
+ /**
+ * Add component to list and initialize it if the connection is up.
+ */
+ public void addHvacTextView(TemperatureView temperatureView) {
+
+ HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
+ if (!mTempComponents.containsKey(hvacKey)) {
+ mTempComponents.put(hvacKey, new ArrayList<>());
+ }
+ mTempComponents.get(hvacKey).add(temperatureView);
+ initComponent(temperatureView);
+ }
+
+ private void initComponents() {
+ Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
+ mTempComponents.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
+ List<TemperatureView> temperatureViews = next.getValue();
+ for (TemperatureView view : temperatureViews) {
+ initComponent(view);
+ }
+ }
+ }
+
+ private void initComponent(TemperatureView view) {
+ int id = view.getPropertyId();
+ int zone = view.getAreaId();
+ try {
+ if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
+ view.setTemp(Float.NaN);
+ return;
+ }
+ view.setTemp(mHvacManager.getFloatProperty(id, zone));
+ } catch (Exception e) {
+ view.setTemp(Float.NaN);
+ Log.e(TAG, "Failed to get value from hvac service", e);
+ }
+ }
/**
* Removes all registered components. This is useful if you need to rebuild the UI since
@@ -200,7 +195,7 @@
int mPropertyId;
int mAreaId;
- public HvacKey(int propertyId, int areaId) {
+ private HvacKey(int propertyId, int areaId) {
mPropertyId = propertyId;
mAreaId = areaId;
}
@@ -210,8 +205,8 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HvacKey hvacKey = (HvacKey) o;
- return mPropertyId == hvacKey.mPropertyId &&
- mAreaId == hvacKey.mAreaId;
+ return mPropertyId == hvacKey.mPropertyId
+ && mAreaId == hvacKey.mAreaId;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 4d8ce43..507c60f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -40,7 +40,7 @@
public TemperatureTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
- mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1);
+ mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1);
mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
mTempFormat = (format == null) ? "%.1f\u00B0" : format;
@@ -48,6 +48,7 @@
/**
* Formats the float for display
+ *
* @param temp - The current temp or NaN
*/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
new file mode 100644
index 0000000..71cc19b
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.content.Context;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.plugins.VolumeDialog;
+
+/**
+ * Allows for adding car specific dialog when the volume dialog is created.
+ */
+public class CarVolumeDialogComponent extends VolumeDialogComponent {
+
+ public CarVolumeDialogComponent(SystemUI sysui, Context context) {
+ super(sysui, context);
+ }
+
+ protected VolumeDialog createDefault() {
+ return new CarVolumeDialogImpl(mContext);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
new file mode 100644
index 0000000..1737b64
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.ServiceConnection;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemAdapter.BackgroundStyle;
+import androidx.car.widget.ListItemProvider.ListProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.SeekbarListItem;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.VolumeDialog;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Car version of the volume dialog.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+public class CarVolumeDialogImpl implements VolumeDialog {
+
+ private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
+
+ private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
+ private static final String XML_TAG_VOLUME_ITEM = "item";
+ private static final int HOVERING_TIMEOUT = 16000;
+ private static final int NORMAL_TIMEOUT = 3000;
+ private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
+ private static final int DISMISS_DELAY_IN_MILLIS = 50;
+ private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
+
+ private final Context mContext;
+ private final H mHandler = new H();
+ // All the volume items.
+ private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
+ // Available volume items in car audio manager.
+ private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
+ // Volume items in the PagedListView.
+ private final List<ListItem> mVolumeLineItems = new ArrayList<>();
+ private final KeyguardManager mKeyguard;
+ private Window mWindow;
+ private CustomDialog mDialog;
+ private PagedListView mListView;
+ private ListItemAdapter mPagedListAdapter;
+ private Car mCar;
+ private CarAudioManager mCarAudioManager;
+ private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
+ new CarAudioManager.CarVolumeCallback() {
+ @Override
+ public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
+ // TODO: Include zoneId into consideration.
+ // For instance
+ // - single display + single-zone, ignore zoneId
+ // - multi-display + single-zone, zoneId is fixed, may show volume bar on all displays
+ // - single-display + multi-zone, may show volume bar on primary display only
+ // - multi-display + multi-zone, may show volume bar on display specified by zoneId
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ int value = getSeekbarValue(mCarAudioManager, groupId);
+ // Do not update the progress if it is the same as before. When car audio manager sets
+ // its group volume caused by the seekbar progress changed, it also triggers this
+ // callback. Updating the seekbar at the same time could block the continuous seeking.
+ if (value != volumeItem.progress) {
+ volumeItem.listItem.setProgress(value);
+ volumeItem.progress = value;
+ }
+ if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+ mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onMasterMuteChanged(int zoneId, int flags) {
+ // ignored
+ }
+ };
+ private boolean mHovering;
+ private boolean mShowing;
+ private boolean mExpanded;
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ mExpanded = false;
+ mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+ // Populates volume slider items from volume groups to UI.
+ for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+ VolumeItem volumeItem = getVolumeItemForUsages(
+ mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+ mAvailableVolumeItems.add(volumeItem);
+ // The first one is the default item.
+ if (groupId == 0) {
+ volumeItem.defaultItem = true;
+ addSeekbarListItem(volumeItem, groupId,
+ R.drawable.car_ic_keyboard_arrow_down,
+ new ExpandIconListener());
+ }
+ }
+
+ // If list is already initiated, update its content.
+ if (mPagedListAdapter != null) {
+ mPagedListAdapter.notifyDataSetChanged();
+ }
+ mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ /**
+ * This does not get called when service is properly disconnected.
+ * So we need to also handle cleanups in destroy().
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ cleanupAudioManager();
+ }
+ };
+
+ public CarVolumeDialogImpl(Context context) {
+ mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
+ mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mCar = Car.createCar(mContext, mServiceConnection);
+ }
+
+ private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+ try {
+ return carAudioManager.getGroupVolume(volumeGroupId);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ return 0;
+ }
+
+ private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+ try {
+ return carAudioManager.getGroupMaxVolume(volumeGroupId);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ return 0;
+ }
+
+ /**
+ * Build the volume window and connect to the CarService which registers with car audio
+ * manager.
+ */
+ @Override
+ public void init(int windowType, Callback callback) {
+ initDialog();
+
+ mCar.connect();
+ }
+
+ @Override
+ public void destroy() {
+ mHandler.removeCallbacksAndMessages(null);
+
+ cleanupAudioManager();
+ // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
+ // audio manager beforehand.
+ mCar.disconnect();
+ }
+
+ private void initDialog() {
+ loadAudioUsageItems();
+ mVolumeLineItems.clear();
+ mDialog = new CustomDialog(mContext);
+
+ mHovering = false;
+ mShowing = false;
+ mExpanded = false;
+ mWindow = mDialog.getWindow();
+ mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
+ final WindowManager.LayoutParams lp = mWindow.getAttributes();
+ lp.format = PixelFormat.TRANSLUCENT;
+ lp.setTitle(VolumeDialogImpl.class.getSimpleName());
+ lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ lp.windowAnimations = -1;
+ mWindow.setAttributes(lp);
+ mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ mDialog.setCanceledOnTouchOutside(true);
+ mDialog.setContentView(R.layout.car_volume_dialog);
+ mDialog.setOnShowListener(dialog -> {
+ mListView.setTranslationY(-mListView.getHeight());
+ mListView.setAlpha(0);
+ mListView.animate()
+ .alpha(1)
+ .translationY(0)
+ .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+ .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+ .start();
+ });
+ mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
+ mListView.setOnHoverListener((v, event) -> {
+ int action = event.getActionMasked();
+ mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
+ || (action == MotionEvent.ACTION_HOVER_MOVE);
+ rescheduleTimeoutH();
+ return true;
+ });
+
+ mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
+ BackgroundStyle.PANEL);
+ mListView.setAdapter(mPagedListAdapter);
+ mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+ }
+
+
+ private void showH(int reason) {
+ if (D.BUG) {
+ Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
+ }
+
+ mHandler.removeMessages(H.SHOW);
+ mHandler.removeMessages(H.DISMISS);
+ rescheduleTimeoutH();
+ // Refresh the data set before showing.
+ mPagedListAdapter.notifyDataSetChanged();
+ if (mShowing) {
+ return;
+ }
+ mShowing = true;
+
+ mDialog.show();
+ Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+ }
+
+ private void rescheduleTimeoutH() {
+ mHandler.removeMessages(H.DISMISS);
+ final int timeout = computeTimeoutH();
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
+
+ if (D.BUG) {
+ Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
+ }
+ }
+
+ private int computeTimeoutH() {
+ return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
+ }
+
+ private void dismissH(int reason) {
+ if (D.BUG) {
+ Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
+ }
+
+ mHandler.removeMessages(H.DISMISS);
+ mHandler.removeMessages(H.SHOW);
+ if (!mShowing) {
+ return;
+ }
+
+ mListView.animate().cancel();
+
+ mListView.setTranslationY(0);
+ mListView.setAlpha(1);
+ mListView.animate()
+ .alpha(0)
+ .translationY(-mListView.getHeight())
+ .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+ .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+ .withEndAction(() -> mHandler.postDelayed(() -> {
+ if (D.BUG) {
+ Log.d(TAG, "mDialog.dismiss()");
+ }
+ mDialog.dismiss();
+ mShowing = false;
+ }, DISMISS_DELAY_IN_MILLIS))
+ .start();
+
+ Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+ }
+
+ private void loadAudioUsageItems() {
+ try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+ int type;
+ // Traverse to the first start tag
+ while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+ && type != XmlResourceParser.START_TAG) {
+ // Do Nothing (moving parser to start element)
+ }
+
+ if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
+ throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
+ }
+ int outerDepth = parser.getDepth();
+ int rank = 0;
+ while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+ && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlResourceParser.END_TAG) {
+ continue;
+ }
+ if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
+ TypedArray item = mContext.getResources().obtainAttributes(
+ attrs, R.styleable.carVolumeItems_item);
+ int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
+ if (usage >= 0) {
+ VolumeItem volumeItem = new VolumeItem();
+ volumeItem.rank = rank;
+ volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon,
+ 0);
+ mVolumeItems.put(usage, volumeItem);
+ rank++;
+ }
+ item.recycle();
+ }
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Error parsing volume groups configuration", e);
+ }
+ }
+
+ private VolumeItem getVolumeItemForUsages(int[] usages) {
+ int rank = Integer.MAX_VALUE;
+ VolumeItem result = null;
+ for (int usage : usages) {
+ VolumeItem volumeItem = mVolumeItems.get(usage);
+ if (volumeItem.rank < rank) {
+ rank = volumeItem.rank;
+ result = volumeItem;
+ }
+ }
+ return result;
+ }
+
+ private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem,
+ int volumeGroupId,
+ int supplementalIconId,
+ @Nullable View.OnClickListener supplementalIconOnClickListener) {
+ SeekbarListItem listItem = new SeekbarListItem(mContext);
+ listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
+ int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
+ int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
+ listItem.setProgress(progress);
+ listItem.setOnSeekBarChangeListener(new CarVolumeDialogImpl
+ .VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
+ Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
+ primaryIcon.mutate().setTint(color);
+ listItem.setPrimaryActionIcon(primaryIcon);
+ if (supplementalIconId != 0) {
+ Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
+ supplementalIcon.mutate().setTint(color);
+ listItem.setSupplementalIcon(supplementalIcon, true);
+ listItem.setSupplementalIconListener(supplementalIconOnClickListener);
+ } else {
+ listItem.setSupplementalEmptyIcon(true);
+ listItem.setSupplementalIconListener(null);
+ }
+
+ mVolumeLineItems.add(listItem);
+ volumeItem.listItem = listItem;
+ volumeItem.progress = progress;
+ return listItem;
+ }
+
+ private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
+ for (int i = 0; i < mVolumeItems.size(); ++i) {
+ VolumeItem volumeItem = mVolumeItems.valueAt(i);
+ if (volumeItem.listItem == targetItem) {
+ return volumeItem;
+ }
+ }
+ return null;
+ }
+
+ private void cleanupAudioManager() {
+ mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback);
+ mVolumeLineItems.clear();
+ mCarAudioManager = null;
+ }
+
+ /**
+ * Wrapper class which contains information of each volume group.
+ */
+ private static class VolumeItem {
+
+ private int rank;
+ private boolean defaultItem = false;
+ private @DrawableRes int icon;
+ private SeekbarListItem listItem;
+ private int progress;
+ }
+
+ private final class H extends Handler {
+
+ private static final int SHOW = 1;
+ private static final int DISMISS = 2;
+
+ private H() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW:
+ showH(msg.arg1);
+ break;
+ case DISMISS:
+ dismissH(msg.arg1);
+ break;
+ default:
+ }
+ }
+ }
+
+ private final class CustomDialog extends Dialog implements DialogInterface {
+
+ private CustomDialog(Context context) {
+ super(context, com.android.systemui.R.style.qs_theme);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ rescheduleTimeoutH();
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ protected void onStart() {
+ super.setCanceledOnTouchOutside(true);
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (isShowing()) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mHandler.obtainMessage(
+ H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private final class ExpandIconListener implements View.OnClickListener {
+
+ @Override
+ public void onClick(final View v) {
+ mExpanded = !mExpanded;
+ Animator inAnimator;
+ if (mExpanded) {
+ for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
+ // Adding the items which are not coming from the default item.
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ if (volumeItem.defaultItem) {
+ // Set progress here due to the progress of seekbar may not be updated.
+ volumeItem.listItem.setProgress(volumeItem.progress);
+ } else {
+ addSeekbarListItem(volumeItem, groupId, 0, null);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_up);
+ } else {
+ // Only keeping the default stream if it is not expended.
+ Iterator itr = mVolumeLineItems.iterator();
+ while (itr.hasNext()) {
+ SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
+ VolumeItem volumeItem = findVolumeItem(seekbarListItem);
+ if (!volumeItem.defaultItem) {
+ itr.remove();
+ } else {
+ // Set progress here due to the progress of seekbar may not be updated.
+ seekbarListItem.setProgress(volumeItem.progress);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_down);
+ }
+
+ Animator outAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_out);
+ inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
+ AnimatorSet animators = new AnimatorSet();
+ animators.playTogether(outAnimator, inAnimator);
+ animators.setTarget(v);
+ animators.start();
+ mPagedListAdapter.notifyDataSetChanged();
+ }
+ }
+
+ private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+
+ private final int mVolumeGroupId;
+ private final CarAudioManager mCarAudioManager;
+
+ private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
+ mVolumeGroupId = volumeGroupId;
+ mCarAudioManager = carAudioManager;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (!fromUser) {
+ // For instance, if this event is originated from AudioService,
+ // we can ignore it as it has already been handled and doesn't need to be
+ // sent back down again.
+ return;
+ }
+ try {
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+ return;
+ }
+ mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+ mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ }
+}
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index 7057ce6..aef6a3c 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -9,4 +9,5 @@
jminjie@google.com
satk@google.com
shuoq@google.com
-refuhoo@google.com
\ No newline at end of file
+refuhoo@google.com
+nazaninb@google.com
\ No newline at end of file
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 617e49a..a9a5450 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -22,5 +22,6 @@
<string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
<string-array name="autofill_field_classification_available_algorithms">
<item>EDIT_DISTANCE</item>
+ <item>EXACT_MATCH</item>
</string-array>
</resources>
diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
index 9ba7e09..e379db8 100644
--- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
@@ -15,8 +15,6 @@
*/
package android.ext.services.autofill;
-import static android.ext.services.autofill.EditDistanceScorer.DEFAULT_ALGORITHM;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -26,27 +24,72 @@
import com.android.internal.util.ArrayUtils;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
private static final String TAG = "AutofillFieldClassificationServiceImpl";
+ private static final String DEFAULT_ALGORITHM = REQUIRED_ALGORITHM_EDIT_DISTANCE;
+
@Nullable
@Override
- public float[][] onGetScores(@Nullable String algorithmName,
- @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues,
- @NonNull List<String> userDataValues) {
+ /** @hide */
+ public float[][] onCalculateScores(@NonNull List<AutofillValue> actualValues,
+ @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable Map algorithms, @Nullable Map args) {
if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
- Log.w(TAG, "getScores(): empty currentvalues (" + actualValues + ") or userValues ("
- + userDataValues + ")");
+ Log.w(TAG, "calculateScores(): empty currentvalues (" + actualValues
+ + ") or userValues (" + userDataValues + ")");
return null;
}
- if (algorithmName != null && !algorithmName.equals(DEFAULT_ALGORITHM)) {
- Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using "
- + DEFAULT_ALGORITHM + " instead");
- }
- return EditDistanceScorer.getScores(actualValues, userDataValues);
+ return calculateScores(actualValues, userDataValues, categoryIds, defaultAlgorithm,
+ defaultArgs, (HashMap<String, String>) algorithms,
+ (HashMap<String, Bundle>) args);
+ }
+
+ /** @hide */
+ public float[][] calculateScores(@NonNull List<AutofillValue> actualValues,
+ @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable HashMap<String, String> algorithms,
+ @Nullable HashMap<String, Bundle> args) {
+ final int actualValuesSize = actualValues.size();
+ final int userDataValuesSize = userDataValues.size();
+ final float[][] scores = new float[actualValuesSize][userDataValuesSize];
+
+ for (int j = 0; j < userDataValuesSize; j++) {
+ final String categoryId = categoryIds.get(j);
+ String algorithmName = defaultAlgorithm;
+ Bundle arg = defaultArgs;
+ if (algorithms != null && algorithms.containsKey(categoryId)) {
+ algorithmName = algorithms.get(categoryId);
+ }
+ if (args != null && args.containsKey(categoryId)) {
+ arg = args.get(categoryId);
+ }
+
+ if (algorithmName == null || (!algorithmName.equals(DEFAULT_ALGORITHM)
+ && !algorithmName.equals(REQUIRED_ALGORITHM_EXACT_MATCH))) {
+ Log.w(TAG, "algorithmName is " + algorithmName + ", defaulting to "
+ + DEFAULT_ALGORITHM);
+ algorithmName = DEFAULT_ALGORITHM;
+ }
+
+ for (int i = 0; i < actualValuesSize; i++) {
+ if (algorithmName.equals(DEFAULT_ALGORITHM)) {
+ scores[i][j] = EditDistanceScorer.calculateScore(actualValues.get(i),
+ userDataValues.get(j));
+ } else {
+ scores[i][j] = ExactMatch.calculateScore(actualValues.get(i),
+ userDataValues.get(j), arg);
+ }
+ }
+ }
+ return scores;
}
}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
index 302b160..6a47901 100644
--- a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
+++ b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
@@ -17,13 +17,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.util.Log;
import android.view.autofill.AutofillValue;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.List;
-
final class EditDistanceScorer {
private static final String TAG = "EditDistanceScorer";
@@ -31,15 +28,14 @@
// TODO(b/70291841): STOPSHIP - set to false before launching
private static final boolean DEBUG = true;
- static final String DEFAULT_ALGORITHM = "EDIT_DISTANCE";
-
/**
* Gets the field classification score of 2 values based on the edit distance between them.
*
* <p>The score is defined as: @(max_length - edit_distance) / max_length
*/
@VisibleForTesting
- static float getScore(@Nullable AutofillValue actualValue, @Nullable String userDataValue) {
+ static float calculateScore(@Nullable AutofillValue actualValue,
+ @Nullable String userDataValue) {
if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
final String actualValueText = actualValue.getTextValue().toString();
@@ -123,26 +119,5 @@
return d[m][n];
}
- /**
- * Gets the scores in a batch.
- */
- static float[][] getScores(@NonNull List<AutofillValue> actualValues,
- @NonNull List<String> userDataValues) {
- final int actualValuesSize = actualValues.size();
- final int userDataValuesSize = userDataValues.size();
- if (DEBUG) {
- Log.d(TAG, "getScores() will return a " + actualValuesSize + "x"
- + userDataValuesSize + " matrix for " + DEFAULT_ALGORITHM);
- }
- final float[][] scores = new float[actualValuesSize][userDataValuesSize];
-
- for (int i = 0; i < actualValuesSize; i++) {
- for (int j = 0; j < userDataValuesSize; j++) {
- final float score = getScore(actualValues.get(i), userDataValues.get(j));
- scores[i][j] = score;
- }
- }
- return scores;
- }
}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
new file mode 100644
index 0000000..3e55c5c
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.autofill;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+final class ExactMatch {
+
+ /**
+ * Gets the field classification score of 2 values based on whether they are an exact match
+ *
+ * @return {@code 1.0} if the two values are an exact match, {@code 0.0} otherwise.
+ */
+ @VisibleForTesting
+ static float calculateScore(@Nullable AutofillValue actualValue,
+ @Nullable String userDataValue, @Nullable Bundle args) {
+ if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
+
+ final String actualValueText = actualValue.getTextValue().toString();
+
+ final int suffixLength;
+ if (args != null) {
+ suffixLength = args.getInt("suffix", -1);
+
+ if (suffixLength < 0) {
+ throw new IllegalArgumentException("suffix argument is invalid");
+ }
+
+ final String actualValueSuffix;
+ if (suffixLength < actualValueText.length()) {
+ actualValueSuffix = actualValueText.substring(actualValueText.length()
+ - suffixLength);
+ } else {
+ actualValueSuffix = actualValueText;
+ }
+
+ final String userDataValueSuffix;
+ if (suffixLength < userDataValue.length()) {
+ userDataValueSuffix = userDataValue.substring(userDataValue.length()
+ - suffixLength);
+ } else {
+ userDataValueSuffix = userDataValue;
+ }
+
+ return (actualValueSuffix.equalsIgnoreCase(userDataValueSuffix)) ? 1 : 0;
+ } else {
+ return actualValueText.equalsIgnoreCase(userDataValue) ? 1 : 0;
+ }
+ }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 4b212c2..133d8ba 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -19,8 +19,7 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,20 +27,15 @@
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageManager;
-import android.database.ContentObserver;
import android.ext.services.notification.AgingHelper.Callback;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationStats;
@@ -93,8 +87,6 @@
PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL);
}
- private float mDismissToViewRatioLimit;
- private int mStreakLimit;
private SmartActionsHelper mSmartActionsHelper;
private NotificationCategorizer mNotificationCategorizer;
private AgingHelper mAgingHelper;
@@ -108,7 +100,11 @@
private Ranking mFakeRanking = null;
private AtomicFile mFile = null;
private IPackageManager mPackageManager;
- protected SettingsObserver mSettingsObserver;
+
+ @VisibleForTesting
+ protected AssistantSettings.Factory mSettingsFactory = AssistantSettings.FACTORY;
+ @VisibleForTesting
+ protected AssistantSettings mSettings;
public Assistant() {
}
@@ -119,7 +115,8 @@
// Contexts are correctly hooked up by the creation step, which is required for the observer
// to be hooked up/initialized.
mPackageManager = ActivityThread.getPackageManager();
- mSettingsObserver = new SettingsObserver(mHandler);
+ mSettings = mSettingsFactory.createAndRegister(mHandler,
+ getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
mSmartActionsHelper = new SmartActionsHelper();
mNotificationCategorizer = new NotificationCategorizer();
mAgingHelper = new AgingHelper(getContext(),
@@ -217,11 +214,11 @@
if (!isForCurrentUser(sbn)) {
return null;
}
- NotificationEntry entry = new NotificationEntry(
- ActivityThread.getPackageManager(), sbn, channel);
+ NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel);
ArrayList<Notification.Action> actions =
- mSmartActionsHelper.suggestActions(this, entry);
- ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(this, entry);
+ mSmartActionsHelper.suggestActions(this, entry, mSettings);
+ ArrayList<CharSequence> replies =
+ mSmartActionsHelper.suggestReplies(this, entry, mSettings);
return createEnqueuedNotificationAdjustment(entry, actions, replies);
}
@@ -240,8 +237,7 @@
if (!smartReplies.isEmpty()) {
signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
}
- if (Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) == 1) {
+ if (mSettings.mNewInterruptionModel) {
if (mNotificationCategorizer.shouldSilence(entry)) {
final int importance = entry.getImportance() < IMPORTANCE_LOW
? entry.getImportance() : IMPORTANCE_LOW;
@@ -461,6 +457,11 @@
}
@VisibleForTesting
+ public void setSmartActionsHelper(SmartActionsHelper smartActionsHelper) {
+ mSmartActionsHelper = smartActionsHelper;
+ }
+
+ @VisibleForTesting
public ChannelImpressions getImpressions(String key) {
synchronized (mkeyToImpressions) {
return mkeyToImpressions.get(key);
@@ -476,10 +477,20 @@
private ChannelImpressions createChannelImpressionsWithThresholds() {
ChannelImpressions impressions = new ChannelImpressions();
- impressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit);
+ impressions.updateThresholds(mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
return impressions;
}
+ private void updateThresholds() {
+ // Update all existing channel impression objects with any new limits/thresholds.
+ synchronized (mkeyToImpressions) {
+ for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
+ channelImpressions.updateThresholds(
+ mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
+ }
+ }
+ }
+
protected final class AgingCallback implements Callback {
@Override
public void sendAdjustment(String key, int newImportance) {
@@ -496,51 +507,4 @@
}
}
- /**
- * Observer for updates on blocking helper threshold values.
- */
- protected final class SettingsObserver extends ContentObserver {
- private final Uri STREAK_LIMIT_URI =
- Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
- private final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
- Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
-
- public SettingsObserver(Handler handler) {
- super(handler);
- ContentResolver resolver = getApplicationContext().getContentResolver();
- resolver.registerContentObserver(
- DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, getUserId());
- resolver.registerContentObserver(STREAK_LIMIT_URI, false, this, getUserId());
-
- // Update all uris on creation.
- update(null);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- update(uri);
- }
-
- private void update(Uri uri) {
- ContentResolver resolver = getApplicationContext().getContentResolver();
- if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
- mDismissToViewRatioLimit = Settings.Global.getFloat(
- resolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
- }
- if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
- mStreakLimit = Settings.Global.getInt(
- resolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
- ChannelImpressions.DEFAULT_STREAK_LIMIT);
- }
-
- // Update all existing channel impression objects with any new limits/thresholds.
- synchronized (mkeyToImpressions) {
- for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
- channelImpressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit);
- }
- }
- }
- }
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
new file mode 100644
index 0000000..39a1676
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ext.services.notification;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Observes the settings for {@link Assistant}.
+ */
+final class AssistantSettings extends ContentObserver {
+ public static Factory FACTORY = AssistantSettings::createAndRegister;
+ private static final boolean DEFAULT_GENERATE_REPLIES = true;
+ private static final boolean DEFAULT_GENERATE_ACTIONS = true;
+ private static final int DEFAULT_NEW_INTERRUPTION_MODEL_INT = 1;
+
+ private static final Uri STREAK_LIMIT_URI =
+ Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
+ private static final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
+ Settings.Global.getUriFor(
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
+ private static final Uri SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI =
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS);
+ private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
+ Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
+
+ private static final String KEY_GENERATE_REPLIES = "generate_replies";
+ private static final String KEY_GENERATE_ACTIONS = "generate_actions";
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private final ContentResolver mResolver;
+ private final int mUserId;
+
+ @VisibleForTesting
+ protected final Runnable mOnUpdateRunnable;
+
+ // Actuall configuration settings.
+ float mDismissToViewRatioLimit;
+ int mStreakLimit;
+ boolean mGenerateReplies = DEFAULT_GENERATE_REPLIES;
+ boolean mGenerateActions = DEFAULT_GENERATE_ACTIONS;
+ boolean mNewInterruptionModel;
+
+ private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
+ Runnable onUpdateRunnable) {
+ super(handler);
+ mResolver = resolver;
+ mUserId = userId;
+ mOnUpdateRunnable = onUpdateRunnable;
+ }
+
+ private static AssistantSettings createAndRegister(
+ Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
+ AssistantSettings assistantSettings =
+ new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
+ assistantSettings.register();
+ return assistantSettings;
+ }
+
+ /**
+ * Creates an instance but doesn't register it as an observer.
+ */
+ @VisibleForTesting
+ protected static AssistantSettings createForTesting(
+ Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
+ return new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
+ }
+
+ private void register() {
+ mResolver.registerContentObserver(
+ DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, mUserId);
+ mResolver.registerContentObserver(STREAK_LIMIT_URI, false, this, mUserId);
+ mResolver.registerContentObserver(
+ SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI, false, this, mUserId);
+
+ // Update all uris on creation.
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ private void update(Uri uri) {
+ if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
+ mDismissToViewRatioLimit = Settings.Global.getFloat(
+ mResolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
+ ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
+ }
+ if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
+ mStreakLimit = Settings.Global.getInt(
+ mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
+ ChannelImpressions.DEFAULT_STREAK_LIMIT);
+ }
+ if (uri == null || SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI.equals(uri)) {
+ mParser.setString(
+ Settings.Global.getString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+ mGenerateReplies =
+ mParser.getBoolean(KEY_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
+ mGenerateActions =
+ mParser.getBoolean(KEY_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
+ }
+ if (uri == null || NOTIFICATION_NEW_INTERRUPTION_MODEL_URI.equals(uri)) {
+ int mNewInterruptionModelInt = Settings.Secure.getInt(
+ mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
+ DEFAULT_NEW_INTERRUPTION_MODEL_INT);
+ mNewInterruptionModel = mNewInterruptionModelInt == 1;
+ }
+
+ mOnUpdateRunnable.run();
+ }
+
+ public interface Factory {
+ AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId,
+ Runnable onUpdateRunnable);
+ }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index b2fc417..38df9b0 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.app.Person;
import android.app.RemoteAction;
import android.content.Context;
import android.os.Bundle;
@@ -31,8 +32,14 @@
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.stream.Collectors;
@@ -48,8 +55,10 @@
| Notification.FLAG_NO_CLEAR;
private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
private static final int MAX_ACTIONS_PER_LINK = 1;
- private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
+ private static final int MAX_SMART_ACTIONS = 3;
private static final int MAX_SUGGESTED_REPLIES = 3;
+ // TODO: Make this configurable.
+ private static final int MAX_MESSAGES_TO_EXTRACT = 5;
private static final ConversationActions.TypeConfig TYPE_CONFIG =
new ConversationActions.TypeConfig.Builder().setIncludedTypes(
@@ -64,13 +73,13 @@
/**
* Adds action adjustments based on the notification contents.
- *
- * TODO: Once we have a API in {@link TextClassificationManager} to predict smart actions
- * from notification text / message, we can replace most of the code here by consuming that API.
*/
@NonNull
- ArrayList<Notification.Action> suggestActions(
- @Nullable Context context, @NonNull NotificationEntry entry) {
+ ArrayList<Notification.Action> suggestActions(@Nullable Context context,
+ @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
+ if (!settings.mGenerateActions) {
+ return EMPTY_ACTION_LIST;
+ }
if (!isEligibleForActionAdjustment(entry)) {
return EMPTY_ACTION_LIST;
}
@@ -81,16 +90,20 @@
if (tcm == null) {
return EMPTY_ACTION_LIST;
}
- Notification.Action[] actions = entry.getNotification().actions;
- int numOfExistingActions = actions == null ? 0: actions.length;
- int maxSmartActions = MAX_SMART_ACTIONS - numOfExistingActions;
+ List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
+ if (messages.isEmpty()) {
+ return EMPTY_ACTION_LIST;
+ }
+ // TODO: Move to TextClassifier.suggestConversationActions once it is ready.
return suggestActionsFromText(
- tcm,
- getMostSalientActionText(entry.getNotification()), maxSmartActions);
+ tcm, messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS);
}
- ArrayList<CharSequence> suggestReplies(
- @Nullable Context context, @NonNull NotificationEntry entry) {
+ ArrayList<CharSequence> suggestReplies(@Nullable Context context,
+ @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
+ if (!settings.mGenerateReplies) {
+ return EMPTY_REPLY_LIST;
+ }
if (!isEligibleForReplyAdjustment(entry)) {
return EMPTY_REPLY_LIST;
}
@@ -101,14 +114,12 @@
if (tcm == null) {
return EMPTY_REPLY_LIST;
}
- CharSequence text = getMostSalientActionText(entry.getNotification());
- ConversationActions.Message message =
- new ConversationActions.Message.Builder()
- .setText(text)
- .build();
-
+ List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
+ if (messages.isEmpty()) {
+ return EMPTY_REPLY_LIST;
+ }
ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
+ new ConversationActions.Request.Builder(messages)
.setMaxSuggestions(MAX_SUGGESTED_REPLIES)
.setHints(HINTS)
.setTypeConfig(TYPE_CONFIG)
@@ -137,10 +148,6 @@
if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
return false;
}
- if (notification.actions != null
- && notification.actions.length >= Notification.MAX_ACTION_BUTTONS) {
- return false;
- }
if ((notification.flags & FLAG_MASK_INELGIBILE_FOR_ACTIONS) != 0) {
return false;
}
@@ -173,21 +180,41 @@
/** Returns the text most salient for action extraction in a notification. */
@Nullable
- private CharSequence getMostSalientActionText(@NonNull Notification notification) {
- /* If it's messaging style, use the most recent message. */
- // TODO: Use the last few X messages instead and take the Person object into consideration.
+ private List<ConversationActions.Message> extractMessages(@NonNull Notification notification) {
Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- if (messages != null && messages.length != 0) {
- Bundle lastMessage = (Bundle) messages[messages.length - 1];
- CharSequence lastMessageText =
- lastMessage.getCharSequence(Notification.MessagingStyle.Message.KEY_TEXT);
- if (!TextUtils.isEmpty(lastMessageText)) {
- return lastMessageText;
+ if (messages == null || messages.length == 0) {
+ return Arrays.asList(new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_REMOTE)
+ .setText(notification.extras.getCharSequence(Notification.EXTRA_TEXT))
+ .build());
+ }
+ Person localUser = notification.extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+ Deque<ConversationActions.Message> extractMessages = new ArrayDeque<>();
+ for (int i = messages.length - 1; i >= 0; i--) {
+ Notification.MessagingStyle.Message message =
+ Notification.MessagingStyle.Message.getMessageFromBundle((Bundle) messages[i]);
+ if (message == null) {
+ continue;
+ }
+ Person senderPerson = message.getSenderPerson();
+ // Skip encoding once the sender is missing as it is important to distinguish
+ // local user and remote user when generating replies.
+ if (senderPerson == null) {
+ break;
+ }
+ Person author = localUser != null && localUser.equals(senderPerson)
+ ? ConversationActions.Message.PERSON_USER_LOCAL : senderPerson;
+ extractMessages.push(new ConversationActions.Message.Builder(author)
+ .setText(message.getText())
+ .setReferenceTime(
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(message.getTimestamp()),
+ ZoneOffset.systemDefault()))
+ .build());
+ if (extractMessages.size() >= MAX_MESSAGES_TO_EXTRACT) {
+ break;
}
}
-
- // Fall back to using the normal text.
- return notification.extras.getCharSequence(Notification.EXTRA_TEXT);
+ return new ArrayList<>(extractMessages);
}
/** Returns a list of actions to act on entities in a given piece of text. */
diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk
index 0a95b85..a57fa94 100644
--- a/packages/ExtServices/tests/Android.mk
+++ b/packages/ExtServices/tests/Android.mk
@@ -12,7 +12,8 @@
mockito-target-minus-junit4 \
espresso-core \
truth-prebuilt \
- testables
+ testables \
+ testng
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
index 48c076e..6fda4c7 100644
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
@@ -16,14 +16,21 @@
package android.ext.services.autofill;
+import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH;
+import static android.view.autofill.AutofillValue.forText;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.os.Bundle;
+import android.view.autofill.AutofillValue;
+
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.view.autofill.AutofillValue;
+import java.util.HashMap;
+import java.util.List;
/**
* Contains the base tests that does not rely on the specific algorithm implementation.
@@ -34,26 +41,78 @@
new AutofillFieldClassificationServiceImpl();
@Test
- public void testOnGetScores_nullActualValues() {
- assertThat(mService.onGetScores(null, null, null, Arrays.asList("whatever"))).isNull();
+ public void testOnCalculateScores_nullActualValues() {
+ assertThat(mService.onCalculateScores(null, null, null, null, null, null, null)).isNull();
}
@Test
- public void testOnGetScores_emptyActualValues() {
- assertThat(mService.onGetScores(null, null, Collections.emptyList(),
- Arrays.asList("whatever"))).isNull();
+ public void testOnCalculateScores_emptyActualValues() {
+ assertThat(mService.onCalculateScores(Collections.emptyList(), Arrays.asList("whatever"),
+ null, null, null, null, null)).isNull();
}
@Test
- public void testOnGetScores_nullUserDataValues() {
- assertThat(mService.onGetScores(null, null,
- Arrays.asList(AutofillValue.forText("whatever")), null)).isNull();
+ public void testOnCalculateScores_nullUserDataValues() {
+ assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
+ null, null, null, null, null, null)).isNull();
}
@Test
- public void testOnGetScores_emptyUserDataValues() {
- assertThat(mService.onGetScores(null, null,
- Arrays.asList(AutofillValue.forText("whatever")), Collections.emptyList()))
- .isNull();
+ public void testOnCalculateScores_emptyUserDataValues() {
+ assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
+ Collections.emptyList(), null, null, null, null, null))
+ .isNull();
+ }
+
+ @Test
+ public void testCalculateScores() {
+ final List<AutofillValue> actualValues = Arrays.asList(forText("A"), forText("b"),
+ forText("dude"));
+ final List<String> userDataValues = Arrays.asList("a", "b", "B", "ab", "c", "dude",
+ "sweet_dude", "dude_sweet");
+ final List<String> categoryIds = Arrays.asList("cat", "cat", "cat", "cat", "cat", "last4",
+ "last4", "last4");
+ final HashMap<String, String> algorithms = new HashMap<>(1);
+ algorithms.put("last4", REQUIRED_ALGORITHM_EXACT_MATCH);
+
+ final Bundle last4Bundle = new Bundle();
+ last4Bundle.putInt("suffix", 4);
+
+ final HashMap<String, Bundle> args = new HashMap<>(1);
+ args.put("last4", last4Bundle);
+
+ final float[][] expectedScores = new float[][] {
+ new float[] { 1F, 0F, 0F, 0.5F, 0F, 0F, 0F, 0F },
+ new float[] { 0F, 1F, 1F, 0.5F, 0F, 0F, 0F, 0F },
+ new float[] { 0F, 0F, 0F, 0F , 0F, 1F, 1F, 0F }
+ };
+ final float[][] actualScores = mService.onCalculateScores(actualValues, userDataValues,
+ categoryIds, null, null, algorithms, args);
+
+ // Unfortunately, Truth does not have an easy way to compare float matrices and show useful
+ // messages in case of error, so we need to check.
+ assertWithMessage("actual=%s, expected=%s", toString(actualScores),
+ toString(expectedScores)).that(actualScores.length).isEqualTo(3);
+ for (int i = 0; i < 3; i++) {
+ assertWithMessage("actual=%s, expected=%s", toString(actualScores),
+ toString(expectedScores)).that(actualScores[i].length).isEqualTo(8);
+ }
+
+ for (int i = 0; i < actualScores.length; i++) {
+ final float[] line = actualScores[i];
+ for (int j = 0; j < line.length; j++) {
+ float cell = line[j];
+ assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F)
+ .of(expectedScores[i][j]);
+ }
+ }
+ }
+
+ public static String toString(float[][] matrix) {
+ final StringBuilder string = new StringBuilder("[ ");
+ for (int i = 0; i < matrix.length; i++) {
+ string.append(Arrays.toString(matrix[i])).append(" ");
+ }
+ return string.append(" ]").toString();
}
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
index afe2236..9b9d4be 100644
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
@@ -15,107 +15,67 @@
*/
package android.ext.services.autofill;
-import static android.ext.services.autofill.EditDistanceScorer.getScore;
-import static android.ext.services.autofill.EditDistanceScorer.getScores;
-import static android.view.autofill.AutofillValue.forText;
+import static android.ext.services.autofill.EditDistanceScorer.calculateScore;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import android.view.autofill.AutofillValue;
import org.junit.Test;
-import java.util.Arrays;
-import java.util.List;
-
public class EditDistanceScorerTest {
@Test
- public void testGetScore_nullValue() {
- assertFloat(getScore(null, "D'OH!"), 0);
+ public void testCalculateScore_nullValue() {
+ assertFloat(calculateScore(null, "D'OH!"), 0);
}
@Test
- public void testGetScore_nonTextValue() {
- assertFloat(getScore(AutofillValue.forToggle(true), "D'OH!"), 0);
+ public void testCalculateScore_nonTextValue() {
+ assertFloat(calculateScore(AutofillValue.forToggle(true), "D'OH!"), 0);
}
@Test
- public void testGetScore_nullUserData() {
- assertFloat(getScore(AutofillValue.forText("D'OH!"), null), 0);
+ public void testCalculateScore_nullUserData() {
+ assertFloat(calculateScore(AutofillValue.forText("D'OH!"), null), 0);
}
@Test
- public void testGetScore_fullMatch() {
- assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1);
- assertFloat(getScore(AutofillValue.forText(""), ""), 1);
+ public void testCalculateScore_fullMatch() {
+ assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1);
+ assertFloat(calculateScore(AutofillValue.forText(""), ""), 1);
}
@Test
- public void testGetScore_fullMatchMixedCase() {
- assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1);
+ public void testCalculateScore_fullMatchMixedCase() {
+ assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1);
}
@Test
- public void testGetScore_mismatchDifferentSizes() {
- assertFloat(getScore(AutofillValue.forText("X"), "Xy"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("Xy"), "X"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F);
- assertFloat(getScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F);
- assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Parkway"),
+ public void testCalculateScore_mismatchDifferentSizes() {
+ assertFloat(calculateScore(AutofillValue.forText("X"), "Xy"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("Xy"), "X"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F);
+ assertFloat(calculateScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F);
+ assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Parkway"),
"1600 Amphitheatre Pkwy"), 0.88F);
- assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Pkwy"),
+ assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Pkwy"),
"1600 Amphitheatre Parkway"), 0.88F);
}
@Test
- public void testGetScore_partialMatch() {
- assertFloat(getScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F);
- assertFloat(getScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F);
- assertFloat(getScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F);
- assertFloat(getScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F);
- }
-
- @Test
- public void testGetScores() {
- final List<AutofillValue> actualValues = Arrays.asList(forText("A"), forText("b"));
- final List<String> userDataValues = Arrays.asList("a", "B", "ab", "c");
- final float[][] expectedScores = new float[][] {
- new float[] { 1F, 0F, 0.5F, 0F },
- new float[] { 0F, 1F, 0.5F, 0F }
- };
- final float[][] actualScores = getScores(actualValues, userDataValues);
-
- // Unfortunately, Truth does not have an easy way to compare float matrices and show useful
- // messages in case of error, so we need to check.
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores.length).isEqualTo(2);
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores[0].length).isEqualTo(4);
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores[1].length).isEqualTo(4);
- for (int i = 0; i < actualScores.length; i++) {
- final float[] line = actualScores[i];
- for (int j = 0; j < line.length; j++) {
- float cell = line[j];
- assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F)
- .of(expectedScores[i][j]);
- }
- }
+ public void testCalculateScore_partialMatch() {
+ assertFloat(calculateScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F);
+ assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F);
+ assertFloat(calculateScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F);
+ assertFloat(calculateScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F);
}
public static void assertFloat(float actualValue, float expectedValue) {
assertThat(actualValue).isWithin(0.01F).of(expectedValue);
}
- public static String toString(float[][] matrix) {
- final StringBuilder string = new StringBuilder("[ ");
- for (int i = 0; i < matrix.length; i++) {
- string.append(Arrays.toString(matrix[i])).append(" ");
- }
- return string.append(" ]").toString();
- }
+
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
new file mode 100644
index 0000000..bf5e160
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.autofill;
+
+import static android.ext.services.autofill.ExactMatch.calculateScore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.os.Bundle;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+
+public class ExactMatchTest {
+
+ private Bundle last4Bundle() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt("suffix", 4);
+ return bundle;
+ }
+
+ @Test
+ public void testCalculateScore_nullValue() {
+ assertFloat(calculateScore(null, "TEST", null), 0);
+ }
+
+ @Test
+ public void testCalculateScore_nonTextValue() {
+ assertFloat(calculateScore(AutofillValue.forToggle(true), "TEST", null), 0);
+ }
+
+ @Test
+ public void testCalculateScore_nullUserData() {
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), null, null), 0);
+ }
+
+ @Test
+ public void testCalculateScore_succeedMatchMixedCases_last4() {
+ final Bundle last4 = last4Bundle();
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "1234 test", last4), 1);
+ assertFloat(calculateScore(AutofillValue.forText("test"), "1234 TEST", last4), 1);
+ }
+
+ @Test
+ public void testCalculateScore_mismatchDifferentSizes_last4() {
+ final Bundle last4 = last4Bundle();
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST1", last4), 0);
+ assertFloat(calculateScore(AutofillValue.forText(""), "TEST", last4), 0);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "", last4), 0);
+ }
+
+ @Test
+ public void testCalculateScore_match() {
+ final Bundle last4 = last4Bundle();
+ assertFloat(calculateScore(AutofillValue.forText("1234 1234 1234 1234"),
+ "xxxx xxxx xxxx 1234", last4), 1);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", null), 1);
+ assertFloat(calculateScore(AutofillValue.forText("TEST 1234"), "1234", last4), 1);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "test", null), 1);
+ }
+
+ @Test
+ public void testCalculateScore_badBundle() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt("suffix", -2);
+ assertThrows(IllegalArgumentException.class, () -> calculateScore(
+ AutofillValue.forText("TEST"), "TEST", bundle));
+
+ final Bundle largeBundle = new Bundle();
+ largeBundle.putInt("suffix", 10);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", largeBundle), 1);
+
+ final Bundle stringBundle = new Bundle();
+ stringBundle.putString("suffix", "value");
+ assertThrows(IllegalArgumentException.class, () -> calculateScore(
+ AutofillValue.forText("TEST"), "TEST", stringBundle));
+
+ }
+
+ public static void assertFloat(float actualValue, float expectedValue) {
+ assertThat(actualValue).isWithin(0.01F).of(expectedValue);
+ }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
new file mode 100644
index 0000000..fd23f2b
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
@@ -0,0 +1,162 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ext.services.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class AssistantSettingsTest {
+ private static final int USER_ID = 5;
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ @Mock Runnable mOnUpdateRunnable;
+
+ private ContentResolver mResolver;
+ private AssistantSettings mAssistantSettings;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mResolver = mContext.getContentResolver();
+ Handler handler = new Handler(Looper.getMainLooper());
+
+ // To bypass real calls to global settings values, set the Settings values here.
+ Settings.Global.putFloat(mResolver,
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
+ Settings.Global.putInt(mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ "generate_replies=true,generate_actions=true");
+ Settings.Secure.putInt(mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+
+ mAssistantSettings = AssistantSettings.createForTesting(
+ handler, mResolver, USER_ID, mOnUpdateRunnable);
+ }
+
+ @Test
+ public void testGenerateRepliesDisabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ "generate_replies=false");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+
+ assertFalse(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testGenerateRepliesEnabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_replies=true");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+ assertTrue(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testGenerateActionsDisabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=false");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+ assertTrue(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testGenerateActionsEnabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=true");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+ assertTrue(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testStreakLimit() {
+ verify(mOnUpdateRunnable, never()).run();
+
+ // Update settings value.
+ int newStreakLimit = 4;
+ Settings.Global.putInt(mResolver,
+ Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
+
+ // Notify for the settings value we updated.
+ mAssistantSettings.onChange(false, Settings.Global.getUriFor(
+ Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
+
+ assertEquals(newStreakLimit, mAssistantSettings.mStreakLimit);
+ verify(mOnUpdateRunnable).run();
+ }
+
+ @Test
+ public void testDismissToViewRatioLimit() {
+ verify(mOnUpdateRunnable, never()).run();
+
+ // Update settings value.
+ float newDismissToViewRatioLimit = 3f;
+ Settings.Global.putFloat(mResolver,
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
+ newDismissToViewRatioLimit);
+
+ // Notify for the settings value we updated.
+ mAssistantSettings.onChange(false, Settings.Global.getUriFor(
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
+
+ assertEquals(newDismissToViewRatioLimit, mAssistantSettings.mDismissToViewRatioLimit, 1e-6);
+ verify(mOnUpdateRunnable).run();
+ }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 2eb005a..0a95b83 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -33,13 +33,11 @@
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.Build;
import android.os.UserHandle;
-import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -86,8 +84,7 @@
@Mock INotificationManager mNoMan;
@Mock AtomicFile mFile;
- @Mock
- IPackageManager mPackageManager;
+ @Mock IPackageManager mPackageManager;
Assistant mAssistant;
Application mApplication;
@@ -108,20 +105,26 @@
new Intent("android.service.notification.NotificationAssistantService");
startIntent.setPackage("android.ext.services");
- // To bypass real calls to global settings values, set the Settings values here.
- Settings.Global.putFloat(mContext.getContentResolver(),
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
mApplication = (Application) InstrumentationRegistry.getInstrumentation().
getTargetContext().getApplicationContext();
// Force the test to use the correct application instead of trying to use a mock application
setApplication(mApplication);
- bindService(startIntent);
+
+ setupService();
mAssistant = getService();
+
+ // Override the AssistantSettings factory.
+ mAssistant.mSettingsFactory = AssistantSettings::createForTesting;
+
+ bindService(startIntent);
+
+ mAssistant.mSettings.mDismissToViewRatioLimit = 0.8f;
+ mAssistant.mSettings.mStreakLimit = 2;
+ mAssistant.mSettings.mNewInterruptionModel = true;
mAssistant.setNoMan(mNoMan);
mAssistant.setFile(mFile);
mAssistant.setPackageManager(mPackageManager);
+
ApplicationInfo info = mock(ApplicationInfo.class);
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
.thenReturn(info);
@@ -408,6 +411,8 @@
mAssistant.writeXml(serializer);
Assistant assistant = new Assistant();
+ // onCreate is not invoked, so settings won't be initialised, unless we do it here.
+ assistant.mSettings = mAssistant.mSettings;
assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
assertEquals(ci1, assistant.getImpressions(key1));
@@ -417,8 +422,6 @@
@Test
public void testSettingsProviderUpdate() {
- ContentResolver resolver = mApplication.getContentResolver();
-
// Set up channels
String key = mAssistant.getKey("pkg1", 1, "channel1");
ChannelImpressions ci = new ChannelImpressions();
@@ -435,19 +438,11 @@
assertEquals(false, ci.shouldTriggerBlock());
// Update settings values.
- float newDismissToViewRatioLimit = 0f;
- int newStreakLimit = 0;
- Settings.Global.putFloat(resolver,
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- newDismissToViewRatioLimit);
- Settings.Global.putInt(resolver,
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
+ mAssistant.mSettings.mDismissToViewRatioLimit = 0f;
+ mAssistant.mSettings.mStreakLimit = 0;
// Notify for the settings values we updated.
- mAssistant.mSettingsObserver.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
- mAssistant.mSettingsObserver.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
+ mAssistant.mSettings.mOnUpdateRunnable.run();
// With the new threshold, the blocking helper should be triggered.
assertEquals(true, ci.shouldTriggerBlock());
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
new file mode 100644
index 0000000..60d31fc
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -0,0 +1,236 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.notification;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.Person;
+import android.content.Context;
+import android.os.Process;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.ConversationActions;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+
+@RunWith(AndroidJUnit4.class)
+public class SmartActionHelperTest {
+
+ private SmartActionsHelper mSmartActionsHelper = new SmartActionsHelper();
+ private Context mContext;
+ @Mock private TextClassifier mTextClassifier;
+ @Mock private NotificationEntry mNotificationEntry;
+ @Mock private StatusBarNotification mStatusBarNotification;
+ private Notification.Builder mNotificationBuilder;
+ private AssistantSettings mSettings;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ mContext.getSystemService(TextClassificationManager.class)
+ .setTextClassifier(mTextClassifier);
+ when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
+ .thenReturn(new ConversationActions(Collections.emptyList()));
+
+ when(mNotificationEntry.getSbn()).thenReturn(mStatusBarNotification);
+ // The notification is eligible to have smart suggestions.
+ when(mNotificationEntry.hasInlineReply()).thenReturn(true);
+ when(mNotificationEntry.isMessaging()).thenReturn(true);
+ when(mStatusBarNotification.getPackageName()).thenReturn("random.app");
+ when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle());
+ mNotificationBuilder = new Notification.Builder(mContext, "channel");
+ mSettings = AssistantSettings.createForTesting(
+ null, null, Process.myUserHandle().getIdentifier(), null);
+ mSettings.mGenerateActions = true;
+ mSettings.mGenerateReplies = true;
+ }
+
+ @Test
+ public void testSuggestReplies_notMessagingApp() {
+ when(mNotificationEntry.isMessaging()).thenReturn(false);
+ ArrayList<CharSequence> textReplies =
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ assertThat(textReplies).isEmpty();
+ }
+
+ @Test
+ public void testSuggestReplies_noInlineReply() {
+ when(mNotificationEntry.hasInlineReply()).thenReturn(false);
+ ArrayList<CharSequence> textReplies =
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ assertThat(textReplies).isEmpty();
+ }
+
+ @Test
+ public void testSuggestReplies_nonMessageStyle() {
+ Notification notification = mNotificationBuilder.setContentText("Where are you?").build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ List<ConversationActions.Message> messages = getMessagesInRequest();
+ assertThat(messages).hasSize(1);
+ MessageSubject.assertThat(messages.get(0)).hasText("Where are you?");
+ }
+
+ @Test
+ public void testSuggestReplies_messageStyle() {
+ Person me = new Person.Builder().setName("Me").build();
+ Person userA = new Person.Builder().setName("A").build();
+ Person userB = new Person.Builder().setName("B").build();
+ Notification.MessagingStyle style =
+ new Notification.MessagingStyle(me)
+ .addMessage("firstMessage", 1000, (Person) null)
+ .addMessage("secondMessage", 2000, me)
+ .addMessage("thirdMessage", 3000, userA)
+ .addMessage("fourthMessage", 4000, userB);
+ Notification notification =
+ mNotificationBuilder
+ .setContentText("You have three new messages")
+ .setStyle(style)
+ .build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ List<ConversationActions.Message> messages = getMessagesInRequest();
+ assertThat(messages).hasSize(3);
+
+ ConversationActions.Message secondMessage = messages.get(0);
+ MessageSubject.assertThat(secondMessage).hasText("secondMessage");
+ MessageSubject.assertThat(secondMessage)
+ .hasPerson(ConversationActions.Message.PERSON_USER_LOCAL);
+ MessageSubject.assertThat(secondMessage)
+ .hasReferenceTime(createZonedDateTimeFromMsUtc(2000));
+
+ ConversationActions.Message thirdMessage = messages.get(1);
+ MessageSubject.assertThat(thirdMessage).hasText("thirdMessage");
+ MessageSubject.assertThat(thirdMessage).hasPerson(userA);
+ MessageSubject.assertThat(thirdMessage)
+ .hasReferenceTime(createZonedDateTimeFromMsUtc(3000));
+
+ ConversationActions.Message fourthMessage = messages.get(2);
+ MessageSubject.assertThat(fourthMessage).hasText("fourthMessage");
+ MessageSubject.assertThat(fourthMessage).hasPerson(userB);
+ MessageSubject.assertThat(fourthMessage)
+ .hasReferenceTime(createZonedDateTimeFromMsUtc(4000));
+ }
+
+ @Test
+ public void testSuggestReplies_messageStyle_noPerson() {
+ Person me = new Person.Builder().setName("Me").build();
+ Notification.MessagingStyle style =
+ new Notification.MessagingStyle(me).addMessage("message", 1000, (Person) null);
+ Notification notification =
+ mNotificationBuilder
+ .setContentText("You have one new message")
+ .setStyle(style)
+ .build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+
+ verify(mTextClassifier, never())
+ .suggestConversationActions(any(ConversationActions.Request.class));
+ }
+
+ private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault());
+ }
+
+ private List<ConversationActions.Message> getMessagesInRequest() {
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+
+ ArgumentCaptor<ConversationActions.Request> argumentCaptor =
+ ArgumentCaptor.forClass(ConversationActions.Request.class);
+ verify(mTextClassifier).suggestConversationActions(argumentCaptor.capture());
+ ConversationActions.Request request = argumentCaptor.getValue();
+ return request.getConversation();
+ }
+
+ private static final class MessageSubject
+ extends Subject<MessageSubject, ConversationActions.Message> {
+
+ private static final SubjectFactory<MessageSubject, ConversationActions.Message> FACTORY =
+ new SubjectFactory<MessageSubject, ConversationActions.Message>() {
+ @Override
+ public MessageSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull ConversationActions.Message subject) {
+ return new MessageSubject(failureStrategy, subject);
+ }
+ };
+
+ private MessageSubject(
+ FailureStrategy failureStrategy, @Nullable ConversationActions.Message subject) {
+ super(failureStrategy, subject);
+ }
+
+ private void hasText(String text) {
+ if (!Objects.equals(text, getSubject().getText().toString())) {
+ failWithBadResults("has text", text, "has", getSubject().getText());
+ }
+ }
+
+ private void hasPerson(Person person) {
+ if (!Objects.equals(person, getSubject().getAuthor())) {
+ failWithBadResults("has author", person, "has", getSubject().getAuthor());
+ }
+ }
+
+ private void hasReferenceTime(ZonedDateTime referenceTime) {
+ if (!Objects.equals(referenceTime, getSubject().getReferenceTime())) {
+ failWithBadResults(
+ "has reference time",
+ referenceTime,
+ "has",
+ getSubject().getReferenceTime());
+ }
+ }
+
+ private static MessageSubject assertThat(ConversationActions.Message message) {
+ return assertAbout(FACTORY).that(message);
+ }
+ }
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index c9ee5c8..1eb4b74 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -224,7 +224,7 @@
root.flags |= Root.FLAG_REMOVABLE_USB;
}
- if (!VolumeInfo.ID_EMULATED_INTERNAL.equals(volume.getId())) {
+ if (volume.getType() != VolumeInfo.TYPE_EMULATED) {
root.flags |= Root.FLAG_SUPPORTS_EJECT;
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 87d6e4a..be817d6 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -23,7 +23,6 @@
import android.location.Criteria;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -34,87 +33,53 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
+class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
private static final String TAG = "FusedLocationProvider";
private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create(
false, false, false, false, true, true, true, Criteria.POWER_LOW,
Criteria.ACCURACY_FINE);
- private static final int MSG_ENABLE = 1;
- private static final int MSG_DISABLE = 2;
- private static final int MSG_SET_REQUEST = 3;
-
+ private final Context mContext;
+ private final Handler mHandler;
private final FusionEngine mEngine;
- private static class RequestWrapper {
- public ProviderRequestUnbundled request;
- public WorkSource source;
- public RequestWrapper(ProviderRequestUnbundled request, WorkSource source) {
- this.request = request;
- this.source = source;
- }
- }
-
- public FusedLocationProvider(Context context) {
- super(TAG, PROPERTIES);
- mEngine = new FusionEngine(context, Looper.myLooper());
-
- // listen for user change
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- context.registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mEngine.switchUser();
- }
- }
- }, UserHandle.ALL, intentFilter, null, mHandler);
- }
-
- /**
- * For serializing requests to mEngine.
- */
- private Handler mHandler = new Handler() {
+ private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() {
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ENABLE:
- mEngine.init(FusedLocationProvider.this);
- break;
- case MSG_DISABLE:
- mEngine.deinit();
- break;
- case MSG_SET_REQUEST:
- {
- RequestWrapper wrapper = (RequestWrapper) msg.obj;
- mEngine.setRequest(wrapper.request, wrapper.source);
- break;
- }
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mEngine.switchUser();
}
}
};
- @Override
- public void onEnable() {
- mHandler.sendEmptyMessage(MSG_ENABLE);
+ FusedLocationProvider(Context context) {
+ super(TAG, PROPERTIES);
+
+ mContext = context;
+ mHandler = new Handler(Looper.myLooper());
+ mEngine = new FusionEngine(context, Looper.myLooper(), this);
}
- @Override
- public void onDisable() {
- mHandler.sendEmptyMessage(MSG_DISABLE);
+ void init() {
+ // listen for user change
+ mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+ }
+
+ void destroy() {
+ mContext.unregisterReceiver(mUserSwitchReceiver);
+ mHandler.post(() -> mEngine.setRequest(null));
}
@Override
public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) {
- mHandler.obtainMessage(MSG_SET_REQUEST, new RequestWrapper(request, source)).sendToTarget();
+ mHandler.post(() -> mEngine.setRequest(request));
}
@Override
public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- // perform synchronously
mEngine.dump(fd, pw, args);
}
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
index 12966cf..75bb5ec 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
@@ -21,27 +21,24 @@
import android.os.IBinder;
public class FusedLocationService extends Service {
+
private FusedLocationProvider mProvider;
@Override
public IBinder onBind(Intent intent) {
if (mProvider == null) {
- mProvider = new FusedLocationProvider(getApplicationContext());
+ mProvider = new FusedLocationProvider(this);
+ mProvider.init();
}
+
return mProvider.getBinder();
}
@Override
- public boolean onUnbind(Intent intent) {
- // make sure to stop performing work
- if (mProvider != null) {
- mProvider.onDisable();
- }
- return false;
- }
-
- @Override
public void onDestroy() {
- mProvider = null;
+ if (mProvider != null) {
+ mProvider.destroy();
+ mProvider = null;
+ }
}
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 7a49524..e4610cf 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -16,14 +16,6 @@
package com.android.location.fused;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
@@ -32,9 +24,16 @@
import android.os.Looper;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.os.WorkSource;
import android.util.Log;
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.LocationRequestUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
public class FusionEngine implements LocationListener {
public interface Callback {
void reportLocation(Location location);
@@ -47,72 +46,35 @@
public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000L; // 11 seconds
- private final Context mContext;
private final LocationManager mLocationManager;
private final Looper mLooper;
+ private final Callback mCallback;
// all fields are only used on mLooper thread. except for in dump() which is not thread-safe
- private Callback mCallback;
private Location mFusedLocation;
private Location mGpsLocation;
private Location mNetworkLocation;
- private boolean mEnabled;
private ProviderRequestUnbundled mRequest;
private final HashMap<String, ProviderStats> mStats = new HashMap<>();
- public FusionEngine(Context context, Looper looper) {
- mContext = context;
+ FusionEngine(Context context, Looper looper, Callback callback) {
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
mNetworkLocation = new Location("");
mNetworkLocation.setAccuracy(Float.MAX_VALUE);
mGpsLocation = new Location("");
mGpsLocation.setAccuracy(Float.MAX_VALUE);
mLooper = looper;
+ mCallback = callback;
mStats.put(GPS, new ProviderStats());
mStats.put(NETWORK, new ProviderStats());
-
- }
-
- public void init(Callback callback) {
- Log.i(TAG, "engine started (" + mContext.getPackageName() + ")");
- mCallback = callback;
- }
-
- /**
- * Called to stop doing any work, and release all resources
- * This can happen when a better fusion engine is installed
- * in a different package, and this one is no longer needed.
- * Called on mLooper thread
- */
- public void deinit() {
- mRequest = null;
- disable();
- Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
}
/** Called on mLooper thread */
- public void enable() {
- if (!mEnabled) {
- mEnabled = true;
- updateRequirements();
- }
- }
-
- /** Called on mLooper thread */
- public void disable() {
- if (mEnabled) {
- mEnabled = false;
- updateRequirements();
- }
- }
-
- /** Called on mLooper thread */
- public void setRequest(ProviderRequestUnbundled request, WorkSource source) {
+ public void setRequest(ProviderRequestUnbundled request) {
mRequest = request;
- mEnabled = request.getReportLocation();
updateRequirements();
}
@@ -120,6 +82,7 @@
public boolean requested;
public long requestTime;
public long minTime;
+
@Override
public String toString() {
return (requested ? " REQUESTED" : " ---");
@@ -154,7 +117,7 @@
}
private void updateRequirements() {
- if (!mEnabled || mRequest == null) {
+ if (mRequest == null || !mRequest.getReportLocation()) {
mRequest = null;
disableProvider(NETWORK);
disableProvider(GPS);
@@ -200,29 +163,30 @@
* Test whether one location (a) is better to use than another (b).
*/
private static boolean isBetterThan(Location locationA, Location locationB) {
- if (locationA == null) {
- return false;
- }
- if (locationB == null) {
- return true;
- }
- // A provider is better if the reading is sufficiently newer. Heading
- // underground can cause GPS to stop reporting fixes. In this case it's
- // appropriate to revert to cell, even when its accuracy is less.
- if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
- return true;
- }
+ if (locationA == null) {
+ return false;
+ }
+ if (locationB == null) {
+ return true;
+ }
+ // A provider is better if the reading is sufficiently newer. Heading
+ // underground can cause GPS to stop reporting fixes. In this case it's
+ // appropriate to revert to cell, even when its accuracy is less.
+ if (locationA.getElapsedRealtimeNanos()
+ > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
+ return true;
+ }
- // A provider is better if it has better accuracy. Assuming both readings
- // are fresh (and by that accurate), choose the one with the smaller
- // accuracy circle.
- if (!locationA.hasAccuracy()) {
- return false;
- }
- if (!locationB.hasAccuracy()) {
- return true;
- }
- return locationA.getAccuracy() < locationB.getAccuracy();
+ // A provider is better if it has better accuracy. Assuming both readings
+ // are fresh (and by that accurate), choose the one with the smaller
+ // accuracy circle.
+ if (!locationA.hasAccuracy()) {
+ return false;
+ }
+ if (!locationB.hasAccuracy()) {
+ return true;
+ }
+ return locationA.getAccuracy() < locationB.getAccuracy();
}
private void updateFusedLocation() {
@@ -252,9 +216,9 @@
}
if (mCallback != null) {
- mCallback.reportLocation(mFusedLocation);
+ mCallback.reportLocation(mFusedLocation);
} else {
- Log.w(TAG, "Location updates received while fusion engine not started");
+ Log.w(TAG, "Location updates received while fusion engine not started");
}
}
@@ -272,19 +236,22 @@
/** Called on mLooper thread */
@Override
- public void onStatusChanged(String provider, int status, Bundle extras) { }
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
/** Called on mLooper thread */
@Override
- public void onProviderEnabled(String provider) { }
+ public void onProviderEnabled(String provider) {
+ }
/** Called on mLooper thread */
@Override
- public void onProviderDisabled(String provider) { }
+ public void onProviderDisabled(String provider) {
+ }
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
- s.append("mEnabled=").append(mEnabled).append(' ').append(mRequest).append('\n');
+ s.append(mRequest).append('\n');
s.append("fused=").append(mFusedLocation).append('\n');
s.append(String.format("gps %s\n", mGpsLocation));
s.append(" ").append(mStats.get(GPS)).append('\n');
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index ad9fc74..4d5dc4e 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -35,13 +35,13 @@
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveniarra"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiarra"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainarra"</string>
- <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiera"</string>
+ <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiarra"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greziera"</string>
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreera"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituaniera"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espainiera (Latinoamerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letoniera"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Pertsiera"</string>
- <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijanera"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijandarra"</string>
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Poloniarra"</string>
</resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 8f254e9..a7de631 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -200,7 +200,7 @@
}
if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) {
if (!device.sendObject(sendObjectInfoResult.getObjectHandle(),
- sendObjectInfoResult.getCompressedSize(), source)) {
+ sendObjectInfoResult.getCompressedSizeLong(), source)) {
throw new IOException("Failed to send contents of a document");
}
}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index eb9ec82..591cf70 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -15,6 +15,8 @@
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="com.google.android.permission.INSTALL_WEARABLE_PACKAGES" />
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
new file mode 100644
index 0000000..2f8966c
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Check box that is displayed in the activity resolver UI for the user
+ to make their selection the preferred activity. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/alertDialogTheme"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ <CheckBox
+ android:id="@+id/clearContributedFiles"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingLeft="8sp"
+ android:visibility="gone"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ <CheckBox
+ android:id="@+id/keepData"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingLeft="8sp"
+ android:visibility="gone"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
index 172e031..2f2d867 100644
--- a/packages/PackageInstaller/res/values-as/strings.xml
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -16,150 +16,78 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_name (7488448184431507488) -->
- <skip />
- <!-- no translation found for install (711829760615509273) -->
- <skip />
- <!-- no translation found for done (6632441120016885253) -->
- <skip />
- <!-- no translation found for cancel (1018267193425558088) -->
- <skip />
- <!-- no translation found for installing (4921993079741206516) -->
- <skip />
- <!-- no translation found for installing_app (1165095864863849422) -->
- <skip />
- <!-- no translation found for install_done (5987363587661783896) -->
- <skip />
- <!-- no translation found for install_confirm_question (8176284075816604590) -->
- <skip />
- <!-- no translation found for install_confirm_question_update (7942235418781274635) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_system (4713001702777910263) -->
- <skip />
- <!-- no translation found for install_failed (5777824004474125469) -->
- <skip />
- <!-- no translation found for install_failed_blocked (8512284352994752094) -->
- <skip />
- <!-- no translation found for install_failed_conflict (3493184212162521426) -->
- <skip />
- <!-- no translation found for install_failed_incompatible (6019021440094927928) -->
- <skip />
- <!-- no translation found for install_failed_incompatible (2890001324362291683) -->
- <skip />
- <!-- no translation found for install_failed_incompatible (7254630419511645826) -->
- <skip />
- <!-- no translation found for install_failed_invalid_apk (8581007676422623930) -->
- <skip />
- <!-- no translation found for install_failed_msg (6298387264270562442) -->
- <skip />
- <!-- no translation found for install_failed_msg (1920009940048975221) -->
- <skip />
- <!-- no translation found for install_failed_msg (6484461562647915707) -->
- <skip />
- <!-- no translation found for launch (3952550563999890101) -->
- <skip />
- <!-- no translation found for unknown_apps_admin_dlg_text (4456572224020176095) -->
- <skip />
- <!-- no translation found for unknown_apps_user_restriction_dlg_text (151020786933988344) -->
- <skip />
- <!-- no translation found for install_apps_user_restriction_dlg_text (2154119597001074022) -->
- <skip />
- <!-- no translation found for ok (7871959885003339302) -->
- <skip />
- <!-- no translation found for manage_applications (5400164782453975580) -->
- <skip />
- <!-- no translation found for out_of_space_dlg_title (4156690013884649502) -->
- <skip />
- <!-- no translation found for out_of_space_dlg_text (8727714096031856231) -->
- <skip />
- <!-- no translation found for app_not_found_dlg_title (5107924008597470285) -->
- <skip />
- <!-- no translation found for app_not_found_dlg_text (5219983779377811611) -->
- <skip />
- <!-- no translation found for user_is_not_allowed_dlg_title (6915293433252210232) -->
- <skip />
- <!-- no translation found for user_is_not_allowed_dlg_text (3468447791330611681) -->
- <skip />
- <!-- no translation found for generic_error_dlg_title (5863195085927067752) -->
- <skip />
- <!-- no translation found for generic_error_dlg_text (5287861443265795232) -->
- <skip />
- <!-- no translation found for uninstall_application_title (4045420072401428123) -->
- <skip />
- <!-- no translation found for uninstall_update_title (824411791011583031) -->
- <skip />
- <!-- no translation found for uninstall_activity_text (1928194674397770771) -->
- <skip />
- <!-- no translation found for uninstall_application_text (3816830743706143980) -->
- <skip />
- <!-- no translation found for uninstall_application_text_all_users (575491774380227119) -->
- <skip />
- <!-- no translation found for uninstall_application_text_user (498072714173920526) -->
- <skip />
- <!-- no translation found for uninstall_update_text (863648314632448705) -->
- <skip />
- <!-- no translation found for uninstall_update_text_multiuser (8992883151333057227) -->
- <skip />
- <!-- no translation found for uninstalling_notification_channel (840153394325714653) -->
- <skip />
- <!-- no translation found for uninstall_failure_notification_channel (1136405866767576588) -->
- <skip />
- <!-- no translation found for uninstalling (8709566347688966845) -->
- <skip />
- <!-- no translation found for uninstalling_app (8866082646836981397) -->
- <skip />
- <!-- no translation found for uninstall_done (439354138387969269) -->
- <skip />
- <!-- no translation found for uninstall_done_app (4588850984473605768) -->
- <skip />
- <!-- no translation found for uninstall_failed (1847750968168364332) -->
- <skip />
- <!-- no translation found for uninstall_failed_app (5506028705017601412) -->
- <skip />
- <!-- no translation found for uninstall_failed_device_policy_manager (785293813665540305) -->
- <skip />
- <!-- no translation found for uninstall_failed_device_policy_manager_of_user (4813104025494168064) -->
- <skip />
- <!-- no translation found for uninstall_all_blocked_profile_owner (2009393666026751501) -->
- <skip />
- <!-- no translation found for uninstall_blocked_profile_owner (6373897407002404848) -->
- <skip />
- <!-- no translation found for uninstall_blocked_device_owner (6724602931761073901) -->
- <skip />
- <!-- no translation found for manage_device_administrators (3092696419363842816) -->
- <skip />
- <!-- no translation found for manage_users (1243995386982560813) -->
- <skip />
- <!-- no translation found for uninstall_failed_msg (2176744834786696012) -->
- <skip />
- <!-- no translation found for Parse_error_dlg_text (1661404001063076789) -->
- <skip />
- <!-- no translation found for wear_not_allowed_dlg_title (8664785993465117517) -->
- <skip />
- <!-- no translation found for wear_not_allowed_dlg_text (704615521550939237) -->
- <skip />
- <!-- no translation found for message_staging (8032722385658438567) -->
- <skip />
- <!-- no translation found for app_name_unknown (6881210203354323926) -->
- <skip />
- <!-- no translation found for untrusted_external_source_warning (6539403649459942547) -->
- <skip />
- <!-- no translation found for untrusted_external_source_warning (1206648674551321364) -->
- <skip />
- <!-- no translation found for untrusted_external_source_warning (7279739265754475165) -->
- <skip />
- <!-- no translation found for anonymous_source_warning (2784902545920822500) -->
- <skip />
- <!-- no translation found for anonymous_source_warning (3939101621438855516) -->
- <skip />
- <!-- no translation found for anonymous_source_warning (5599483539528168566) -->
- <skip />
- <!-- no translation found for anonymous_source_continue (4375745439457209366) -->
- <skip />
- <!-- no translation found for external_sources_settings (4046964413071713807) -->
- <skip />
- <!-- no translation found for wear_app_channel (1960809674709107850) -->
- <skip />
+ <string name="app_name" msgid="7488448184431507488">"পেকেজ ইনষ্টলাৰ"</string>
+ <string name="install" msgid="711829760615509273">"ইনষ্টল কৰক"</string>
+ <string name="done" msgid="6632441120016885253">"সম্পন্ন হ’ল"</string>
+ <string name="cancel" msgid="1018267193425558088">"বাতিল কৰক"</string>
+ <string name="installing" msgid="4921993079741206516">"ইনষ্টল কৰি থকা হৈছে…"</string>
+ <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে…"</string>
+ <string name="install_done" msgid="5987363587661783896">"এপ্ ইনষ্টল কৰা হ\'ল।"</string>
+ <string name="install_confirm_question" msgid="8176284075816604590">"আপুনি এই এপ্লিকেশ্বনটো ইনষ্টল কৰিব বিচাৰেনে?"</string>
+ <string name="install_confirm_question_update" msgid="7942235418781274635">"আপুনি আগৰে পৰা থকা এপ্লিকেশ্বন আপডে\'ট কৰিব বিচাৰেনে? আপোনাৰ আগৰ ডেটা নেহেৰায়।"</string>
+ <string name="install_confirm_question_update_system" msgid="4713001702777910263">"আপুনি এই আগৰে পৰা থকা এপ্লিকেশ্বনটো আপডে\'ট কৰিব বিচাৰেনে? আপোনাৰ আগৰ ডেটা নেহেৰায়।"</string>
+ <string name="install_failed" msgid="5777824004474125469">"এপ্ ইনষ্টল কৰা হোৱা নাই।"</string>
+ <string name="install_failed_blocked" msgid="8512284352994752094">"পেকেজটোৰ ইনষ্টল অৱৰোধ কৰা হৈছে।"</string>
+ <string name="install_failed_conflict" msgid="3493184212162521426">"এপটো ইনষ্টল কৰিব পৰা নগ\'ল কাৰণ ইয়াৰ সৈতে আগৰে পৰা থকা এটা পেকেজৰ সংঘাত হৈছে।"</string>
+ <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"আপোনাৰ টেবলেটৰ সৈতে খাপ নোখোৱাৰ বাবে এপটো ইনষ্টল কৰা নহ\'ল।"</string>
+ <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"আপোনাৰ টিভিত এই এপটো নচলে"</string>
+ <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"আপোনাৰ ফ\'নৰ সৈতে খাপ নোখোৱাৰ বাবে এপটো ইনষ্টল কৰা নহ\'ল।"</string>
+ <string name="install_failed_invalid_apk" msgid="8581007676422623930">"পেকেজটো মান্য নোহোৱাৰ বাবে এপটো ইনষ্টল কৰা নহ\'ল।"</string>
+ <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"আপোনাৰ টে\'বলেটত <xliff:g id="APP_NAME">%1$s</xliff:g> ইনষ্টল কৰিব পৰা নগ\'ল৷"</string>
+ <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"আপোনাৰ টিভিত <xliff:g id="APP_NAME">%1$s</xliff:g> ইনষ্টল কৰিব পৰা নগ\'ল।"</string>
+ <string name="install_failed_msg" product="default" msgid="6484461562647915707">"আপোনাৰ ফ\'নত <xliff:g id="APP_NAME">%1$s</xliff:g> ইনষ্টল কৰিব পৰা নগ\'ল৷"</string>
+ <string name="launch" msgid="3952550563999890101">"খোলক"</string>
+ <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"আপোনাৰ প্ৰশাসকে অজ্ঞাত উৎসৰ পৰা পোৱা এপ্ ইনষ্টল কৰাৰ অনুমতি দিয়া নাই"</string>
+ <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"এই ব্যৱহাৰকাৰীয়ে অজ্ঞাত উৎসৰপৰা পোৱা এপসমূহ ইনষ্টল কৰিব নোৱাৰে"</string>
+ <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"এই ব্যৱহাৰকাৰীজনৰ এপ্ ইনষ্টল কৰাৰ অনুমতি নাই"</string>
+ <string name="ok" msgid="7871959885003339302">"ঠিক আছে"</string>
+ <string name="manage_applications" msgid="5400164782453975580">"এপ্ পৰিচালনা"</string>
+ <string name="out_of_space_dlg_title" msgid="4156690013884649502">"খালী ঠাই নাই"</string>
+ <string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> ইনষ্টল কৰিব পৰা নগ\'ল। কিছু খালী ঠাই উলিয়াই আকৌ চেষ্টা কৰক৷"</string>
+ <string name="app_not_found_dlg_title" msgid="5107924008597470285">"এপটো পোৱা নগ\'ল"</string>
+ <string name="app_not_found_dlg_text" msgid="5219983779377811611">"ইনষ্টল কৰি ৰখা এপৰ তালিকাত এই এপটো পোৱা নগ\'ল।"</string>
+ <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"অনুমতি নাই"</string>
+ <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"বর্তমানৰ ব্যৱহাৰকাৰীজনক এইটো আনইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"</string>
+ <string name="generic_error_dlg_title" msgid="5863195085927067752">"আসোঁৱাহ"</string>
+ <string name="generic_error_dlg_text" msgid="5287861443265795232">"এপ্ আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
+ <string name="uninstall_application_title" msgid="4045420072401428123">"এপ্ আনইনষ্টল কৰক"</string>
+ <string name="uninstall_update_title" msgid="824411791011583031">"আপডে\'ট আনইনষ্টল কৰক"</string>
+ <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> হৈছে তলৰ এপটোৰ এটা অংশ:"</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"আপুনি এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"আপুনি "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ বাবে এই এপটো আনইনষ্টল কৰিব বিচাৰেনে? এপ্লিকেশ্বন আৰু ইয়াৰ ডেটা ডিভাইচটোত থকা "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ পৰা আঁতৰোৱা হ\'ব৷"</string>
+ <string name="uninstall_application_text_user" msgid="498072714173920526">"আপুনি ব্যৱহাৰকাৰীৰ <xliff:g id="USERNAME">%1$s</xliff:g> বাবে এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
+ <string name="uninstall_update_text" msgid="863648314632448705">"এই এপটোৰ ফেক্টৰী সংস্কৰণ ব্যৱহাৰ কৰিব বিচাৰেনে? সকলো ডেটা মচা হ\'ব।"</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"এই এপটোৰ ফেক্টৰী সংস্কৰণ ব্যৱহাৰ কৰিব বিচাৰেনে? সকলো ডেটা মচা হ\'ব। কর্মস্থানৰ প্ৰফাইল থকা ব্যৱহাৰকাৰীৰ লগতে ডিভাইচটোৰ সকলো ব্যৱহাৰকাৰীৰ ওপৰত ইয়াৰ প্ৰভাৱ পৰিব।"</string>
+ <string name="uninstalling_notification_channel" msgid="840153394325714653">"আনইনষ্টল কৰি থকা হৈছে"</string>
+ <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"যিবোৰ আনইনষ্টল পৰা নগ\'ল"</string>
+ <string name="uninstalling" msgid="8709566347688966845">"আনইনষ্টল কৰি থকা হৈছে…"</string>
+ <string name="uninstalling_app" msgid="8866082646836981397">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰি থকা হৈছে…"</string>
+ <string name="uninstall_done" msgid="439354138387969269">"আনইনষ্টল কৰা হ’ল।"</string>
+ <string name="uninstall_done_app" msgid="4588850984473605768">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰা হ\'ল"</string>
+ <string name="uninstall_failed" msgid="1847750968168364332">"আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
+ <string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
+ <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"ডিভাইচৰ সক্ৰিয় প্ৰশাসক এপ্ আনইনষ্টল কৰিব নোৱাৰি"</string>
+ <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g>ৰ সক্ৰিয় ডিভাইচৰ প্ৰশাসকীয় এপ্ আনইনষ্টল কৰিব নোৱাৰি"</string>
+ <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"এই এপটো কিছুসংখ্যক ব্যৱহাৰকাৰী বা প্ৰ\'ফাইলৰ বাবে প্ৰয়োজনীয় আৰু বাকীসকলৰ বাবে ইয়াক আনইনষ্টল কৰা হৈছে"</string>
+ <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"আপোনাৰ প্ৰ\'ফাইলৰ বাবে এই এপটোৰ প্ৰয়োজন আছে গতিকে আনইনষ্টল কৰিব পৰা নাযায়।"</string>
+ <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"এই এপটো আনইনষ্টল কৰিব পৰা নাযায় কাৰণ আপোনাৰ ডিভাইচৰ প্ৰশাসকে এই এপ্ ৰখাটো বাধ্যতামূলক কৰি ৰাখিছে।"</string>
+ <string name="manage_device_administrators" msgid="3092696419363842816">"ডিভাইচৰ প্ৰশাসক এপসমূহ পৰিচালনা কৰক"</string>
+ <string name="manage_users" msgid="1243995386982560813">"ব্যৱহাৰকাৰী পৰিচালনা কৰক"</string>
+ <string name="uninstall_failed_msg" msgid="2176744834786696012">"<xliff:g id="APP_NAME">%1$s</xliff:g> আনইনষ্টল কৰিব নোৱাৰি।"</string>
+ <string name="Parse_error_dlg_text" msgid="1661404001063076789">"পেকেজটো পাৰ্ছ কৰোঁতে এটা সমস্যাই দেখা দিছিল।"</string>
+ <string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android ৱেৰ"</string>
+ <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"ৱেৰ-ত ইনষ্টল/আনইনষ্টল কৰিব পৰা নাযায়।"</string>
+ <string name="message_staging" msgid="8032722385658438567">"এপৰ অন্তিম পর্যায়ৰ পৰীক্ষণ চলি আছে…"</string>
+ <string name="app_name_unknown" msgid="6881210203354323926">"অজ্ঞাত"</string>
+ <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"আপোনাৰ সুৰক্ষাৰ বাবে এই উৎসৰ পৰা অজ্ঞাত এপসমূহ আপোনাৰ টেবলেটত ইনষ্টল কৰাৰ অনুমতি দিয়া হোৱা নাই।"</string>
+ <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"আপোনাৰ সুৰক্ষাৰ বাবে এই উৎসৰ পৰা অজ্ঞাত এপসমূহ আপোনাৰ টিভিত ইনষ্টল কৰাৰ অনুমতি দিয়া হোৱা নাই।"</string>
+ <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"আপোনাৰ সুৰক্ষাৰ বাবে এই উৎসৰ পৰা অজ্ঞাত এপসমূহ আপোনাৰ ফ\'নত ইনষ্টল কৰাৰ অনুমতি দিয়া হোৱা নাই।"</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"আপোনাৰ ফ\'ন আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। আপুনি এই এপটো ইনষ্টল কৰি এপটোৰ ব্যৱহাৰৰ ফলত আপোনাৰ টিভিত হ\'ব পৰা যিকোনো ক্ষতি বা ডেটা ক্ষয়ৰ বাবে আপুনি নিজে দায়ী হ\'ব বুলি সন্মতি দিয়ে।"</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"আপোনাৰ টেবলেট আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। আপুনি এই এপটো ইনষ্টল কৰি এপটোৰ ব্যৱহাৰৰ ফলত আপোনাৰ টিভিত হ\'ব পৰা যিকোনো ক্ষতি বা ডেটা ক্ষয়ৰ বাবে আপুনি নিজে দায়ী হ\'ব বুলি সন্মতি দিয়ে।"</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"আপোনাৰ টিভি আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। আপুনি এই এপটো ইনষ্টল কৰি এপটোৰ ব্যৱহাৰৰ ফলত আপোনাৰ টিভিত হ\'ব পৰা যিকোনো ক্ষতি বা ডেটা ক্ষয়ৰ বাবে আপুনি নিজে দায়ী হ\'ব বুলি সন্মতি দিয়ে।"</string>
+ <string name="anonymous_source_continue" msgid="4375745439457209366">"অব্যাহত ৰাখক"</string>
+ <string name="external_sources_settings" msgid="4046964413071713807">"ছেটিংসমূহ"</string>
+ <string name="wear_app_channel" msgid="1960809674709107850">"ৱেৰ এপসমূহ ইনষ্টল/আনইনষ্টল কৰি থকা হৈছে"</string>
<string name="app_installed_notification_channel_description" msgid="2695385797601574123">"এপ্ ইনষ্টল কৰাৰ জাননী"</string>
<string name="notification_installation_success_message" msgid="6450467996056038442">"সফলতাৰে ইনষ্টল কৰা হ’ল"</string>
<string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” সফলতাৰে ইনষ্টল কৰা হ’ল"</string>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index d751c42..01f3e5b 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -22,17 +22,17 @@
<string name="cancel" msgid="1018267193425558088">"रद्द करा"</string>
<string name="installing" msgid="4921993079741206516">"इंस्टॉल होत आहे…"</string>
<string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> इंस्टॉल होत आहे…"</string>
- <string name="install_done" msgid="5987363587661783896">"अॅप इंस्टॉल झाले."</string>
+ <string name="install_done" msgid="5987363587661783896">"अॅप इंस्टॉल झाले."</string>
<string name="install_confirm_question" msgid="8176284075816604590">"तुम्हाला हे अॅप्लिकेशन इंस्टॉल करायचे आहे का?"</string>
<string name="install_confirm_question_update" msgid="7942235418781274635">"तुम्हाच्या विद्यमान अॅप्लिकेशनवर अपडेट इंस्टॉल करायचे आहे का? तुमचा विद्यमान डेटा गमावणार नाही."</string>
<string name="install_confirm_question_update_system" msgid="4713001702777910263">"तुम्हाला या बिल्ट-इन अॅप्लिकेशनवर अपडेट इंस्टॉल करायचे आहे का? तुमचा विद्यमान डेटा गमावणार नाही."</string>
- <string name="install_failed" msgid="5777824004474125469">"अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed" msgid="5777824004474125469">"अॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"पॅकेज इंस्टॉल होण्यापासून ब्लॉक केले होते."</string>
- <string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"तुमच्या टॅबलेटशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"हे अॅप तुमच्या टीव्हीशी कंपॅटिबल नाही."</string>
- <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"तुमच्या फोनशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_invalid_apk" msgid="8581007676422623930">"पॅकेज अयोग्य असल्याचे दिसत असल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"तुमच्या टॅबलेटशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"हे अॅप तुमच्या टीव्हीशी कंपॅटिबल नाही."</string>
+ <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"तुमच्या फोनशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_invalid_apk" msgid="8581007676422623930">"पॅकेज अयोग्य असल्याचे दिसत असल्याने अॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या टॅबलेटवर इंस्टॉल केले जाऊ शकत नाही."</string>
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या टीव्हीवर इंस्टॉल केले जाऊ शकत नाही."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या फोनवर इंस्टॉल केले जाऊ शकत नाही."</string>
@@ -44,20 +44,20 @@
<string name="manage_applications" msgid="5400164782453975580">"अॅप्स व्यवस्थापन"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"जागा संपली"</string>
<string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> इंस्टॉल केले जाऊ शकत नाही. काही जागा मोकळी करा आणि पुन्हा प्रयत्न करा."</string>
- <string name="app_not_found_dlg_title" msgid="5107924008597470285">"अॅप आढळले नाही"</string>
- <string name="app_not_found_dlg_text" msgid="5219983779377811611">"इंस्टॉल केलेल्या अॅप्सच्या सूचीमध्ये अॅप आढळले नाही."</string>
+ <string name="app_not_found_dlg_title" msgid="5107924008597470285">"अॅप आढळले नाही"</string>
+ <string name="app_not_found_dlg_text" msgid="5219983779377811611">"इंस्टॉल केलेल्या अॅप्सच्या सूचीमध्ये अॅप आढळले नाही."</string>
<string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"अनुमती नाही"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"हे अनइंस्टॉल करण्याची विद्यमान वापरकर्त्यास अनुमती नाही."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"एरर"</string>
- <string name="generic_error_dlg_text" msgid="5287861443265795232">"अॅप अनइंस्टॉल करणे शक्य झाले नाही."</string>
- <string name="uninstall_application_title" msgid="4045420072401428123">"अॅप अनइंस्टॉल करा"</string>
+ <string name="generic_error_dlg_text" msgid="5287861443265795232">"अॅप अनइंस्टॉल करणे शक्य झाले नाही."</string>
+ <string name="uninstall_application_title" msgid="4045420072401428123">"अॅप अनइंस्टॉल करा"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"अपडेट अनइंस्टॉल करा"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> खालील अॅपचा भाग आहे:"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"तुम्हाला हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
- <string name="uninstall_application_text_all_users" msgid="575491774380227119">"तुम्हाला हे अॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करायचे आहे का? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांकडून काढला जाईल."</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"तुम्हाला हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"तुम्हाला हे अॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करायचे आहे का? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांकडून काढला जाईल."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"तुम्हाला <xliff:g id="USERNAME">%1$s</xliff:g> वापरकर्त्यासाठी हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
- <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
- <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
+ <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"अनइंस्टॉल रन होत आहेत"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"अनइंस्टॉल करता आले नाही"</string>
<string name="uninstalling" msgid="8709566347688966845">"अनइंस्टॉल करत आहे…"</string>
@@ -68,7 +68,7 @@
<string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल करता आले नाही."</string>
<string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"अॅक्टिव्ह डिव्हाइस प्रशासक अॅप अनइंस्टॉल करू शकत नाही"</string>
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी अॅक्टिव्ह डिव्हाइस प्रशासक अॅप अनइंस्टॉल करू शकत नाही"</string>
- <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"हे अॅप काही वापरकर्ते किंवा प्रोफाइलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल करण्यात आले"</string>
+ <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"हे अॅप काही वापरकर्ते किंवा प्रोफाइलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल करण्यात आले"</string>
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"तुमच्या प्रोफाइलसाठी हे अॅप आवश्यक आहे आणि अनइंस्टॉल केले जाऊ शकत नाही."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"तुमच्या डिव्हाइस प्रशासकास हे अॅप आवश्यक आहे आणि ते अनइंस्टॉल केले जाऊ शकत नाही."</string>
<string name="manage_device_administrators" msgid="3092696419363842816">"डिव्हाइस प्रशासक अॅप्स व्यवस्थापित करा"</string>
@@ -77,18 +77,18 @@
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"पॅकेज पार्स करण्यात समस्या आली."</string>
<string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
<string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"इंस्टॉल करा/अनइंस्टॉल करा क्रिया Wear वर सपोर्ट करत नाहीत."</string>
- <string name="message_staging" msgid="8032722385658438567">"अॅप सुरुवातीच्या स्थितीत आहे…"</string>
+ <string name="message_staging" msgid="8032722385658438567">"अॅप सुरुवातीच्या स्थितीत आहे…"</string>
<string name="app_name_unknown" msgid="6881210203354323926">"अज्ञात"</string>
<string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"तुमच्या सुरक्षिततेसाठी, तुमच्या टॅबलेटला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"तुमच्या सुरक्षिततेसाठी, तुमच्या टीव्हीला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
<string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"तुमच्या सुरक्षिततेसाठी, तुमच्या फोनला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने होणार्या तुमच्या फोनचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टॅबलेटचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तुमचा टीव्ही आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टीव्हीचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने होणार्या तुमच्या फोनचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टॅबलेटचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तुमचा टीव्ही आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टीव्हीचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"सुरू ठेवा"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"सेटिंग्ज"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"wear अॅप्स इंस्टॉल/अनइंस्टॉल करत आहे"</string>
- <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अॅप इंस्टॉल सूचना"</string>
+ <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अॅप इंस्टॉल सूचना"</string>
<string name="notification_installation_success_message" msgid="6450467996056038442">"यशस्वीरित्या इंस्टॉल केले"</string>
<string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" यशस्वीरित्या इंस्टॉल झाले"</string>
</resources>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 0f065ab..1e0ff50 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -119,6 +119,10 @@
<string name="uninstall_update_text">Replace this app with the factory version? All data will be removed.</string>
<!-- [CHAR LIMIT=none] -->
<string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
+ <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
+ <string name="uninstall_remove_contributed_files">Also remove <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of associated media files.</string>
+ <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
+ <string name="uninstall_keep_data">Keep <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of app data.</string>
<!-- Label for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] -->
<string name="uninstalling_notification_channel">Running uninstalls</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
index 1c0aec1..63d8c5a8 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -50,6 +50,9 @@
"com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+ static final String EXTRA_CLEAR_CONTRIBUTED_FILES =
+ "com.android.packageinstaller.extra.CLEAR_CONTRIBUTED_FILES";
+ static final String EXTRA_KEEP_DATA = "com.android.packageinstaller.extra.KEEP_DATA";
private int mUninstallId;
private ApplicationInfo mAppInfo;
@@ -72,6 +75,9 @@
if (savedInstanceState == null) {
boolean allUsers = getIntent().getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS,
false);
+ boolean clearContributedFiles = getIntent().getBooleanExtra(
+ EXTRA_CLEAR_CONTRIBUTED_FILES, false);
+ boolean keepData = getIntent().getBooleanExtra(EXTRA_KEEP_DATA, false);
UserHandle user = getIntent().getParcelableExtra(Intent.EXTRA_USER);
// Show dialog, which is the whole UI
@@ -95,12 +101,16 @@
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId,
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0;
+ flags |= clearContributedFiles ? PackageManager.DELETE_CONTRIBUTED_MEDIA : 0;
+ flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
+
try {
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mAppInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
- getPackageName(), allUsers ? PackageManager.DELETE_ALL_USERS : 0,
- pendingIntent.getIntentSender(), user.getIdentifier());
+ getPackageName(), flags, pendingIntent.getIntentSender(),
+ user.getIdentifier());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 1a01dc0..5419449 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -285,7 +285,7 @@
fragment.show(ft, "dialog");
}
- public void startUninstallProgress() {
+ public void startUninstallProgress(boolean clearContributedFiles, boolean keepData) {
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
@@ -310,6 +310,9 @@
newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label);
+ newIntent.putExtra(UninstallUninstalling.EXTRA_CLEAR_CONTRIBUTED_FILES,
+ clearContributedFiles);
+ newIntent.putExtra(UninstallUninstalling.EXTRA_KEEP_DATA, keepData);
newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
if (returnResult) {
@@ -358,12 +361,15 @@
try {
Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras());
+ int flags = mDialogInfo.allUsers ? PackageManager.DELETE_ALL_USERS : 0;
+ flags |= clearContributedFiles ? PackageManager.DELETE_CONTRIBUTED_MEDIA : 0;
+ flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
+
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mDialogInfo.appInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
- getPackageName(), mDialogInfo.allUsers
- ? PackageManager.DELETE_ALL_USERS : 0,
- pendingIntent.getIntentSender(), mDialogInfo.user.getIdentifier());
+ getPackageName(), flags, pendingIntent.getIntentSender(),
+ mDialogInfo.user.getIdentifier());
} catch (Exception e) {
notificationManager.cancel(uninstallId);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index e0ca74e..499da75 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -16,21 +16,155 @@
package com.android.packageinstaller.handheld;
+import static android.os.storage.StorageManager.convert;
+import static android.text.format.Formatter.formatFileSize;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
import com.android.packageinstaller.R;
import com.android.packageinstaller.UninstallerActivity;
+import java.io.IOException;
+import java.util.List;
+
public class UninstallAlertDialogFragment extends DialogFragment implements
DialogInterface.OnClickListener {
+ private static final String LOG_TAG = UninstallAlertDialogFragment.class.getSimpleName();
+
+ private @Nullable CheckBox mClearContributedFiles;
+ private @Nullable CheckBox mKeepData;
+
+ /**
+ * Get number of bytes of the files contributed by the package.
+ *
+ * @param pkg The package that might have contributed files.
+ * @param user The user the package belongs to.
+ *
+ * @return The number of bytes.
+ */
+ private long getContributedMediaSizeForUser(@NonNull String pkg, @NonNull UserHandle user) {
+ try {
+ return MediaStore.getContributedMediaSize(getContext(), pkg, user);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Cannot determine amount of contributes files for " + pkg
+ + " (user " + user + ")", e);
+ return 0;
+ }
+ }
+
+ /**
+ * Get number of bytes of the files contributed by the package.
+ *
+ * @param pkg The package that might have contributed files.
+ * @param user The user the package belongs to or {@code null} if files of all users should be
+ * counted.
+ *
+ * @return The number of bytes.
+ */
+ private long getContributedMediaSize(@NonNull String pkg, @Nullable UserHandle user) {
+ UserManager userManager = getContext().getSystemService(UserManager.class);
+
+ long contributedFileSize = 0;
+
+ if (user == null) {
+ List<UserInfo> users = userManager.getUsers();
+
+ int numUsers = users.size();
+ for (int i = 0; i < numUsers; i++) {
+ contributedFileSize += getContributedMediaSizeForUser(pkg,
+ UserHandle.of(users.get(i).id));
+ }
+ } else {
+ contributedFileSize = getContributedMediaSizeForUser(pkg, user);
+ }
+
+ return contributedFileSize;
+ }
+
+ /**
+ * Get number of bytes of the app data of the package.
+ *
+ * @param pkg The package that might have app data.
+ * @param user The user the package belongs to
+ *
+ * @return The number of bytes.
+ */
+ private long getAppDataSizeForUser(@NonNull String pkg, @NonNull UserHandle user) {
+ StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ StorageStatsManager storageStatsManager =
+ getContext().getSystemService(StorageStatsManager.class);
+
+ List<StorageVolume> volumes = storageManager.getStorageVolumes();
+ long appDataSize = 0;
+
+ int numVolumes = volumes.size();
+ for (int i = 0; i < numVolumes; i++) {
+ StorageStats stats;
+ try {
+ stats = storageStatsManager.queryStatsForPackage(convert(volumes.get(i).getUuid()),
+ pkg, user);
+ } catch (PackageManager.NameNotFoundException | IOException e) {
+ Log.e(LOG_TAG, "Cannot determine amount of app data for " + pkg + " on "
+ + volumes.get(i) + " (user " + user + ")", e);
+ continue;
+ }
+
+ appDataSize += stats.getDataBytes();
+ }
+
+ return appDataSize;
+ }
+
+ /**
+ * Get number of bytes of the app data of the package.
+ *
+ * @param pkg The package that might have app data.
+ * @param user The user the package belongs to or {@code null} if files of all users should be
+ * counted.
+ *
+ * @return The number of bytes.
+ */
+ private long getAppDataSize(@NonNull String pkg, @Nullable UserHandle user) {
+ UserManager userManager = getContext().getSystemService(UserManager.class);
+
+ long appDataSize = 0;
+
+ if (user == null) {
+ List<UserInfo> users = userManager.getUsers();
+
+ int numUsers = users.size();
+ for (int i = 0; i < numUsers; i++) {
+ appDataSize += getAppDataSizeForUser(pkg, UserHandle.of(users.get(i).id));
+ }
+ } else {
+ appDataSize = getAppDataSizeForUser(pkg, user);
+ }
+
+ return appDataSize;
+ }
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
@@ -76,14 +210,61 @@
dialogBuilder.setTitle(appLabel);
dialogBuilder.setPositiveButton(android.R.string.ok, this);
dialogBuilder.setNegativeButton(android.R.string.cancel, this);
- dialogBuilder.setMessage(messageBuilder.toString());
+
+ String pkg = dialogInfo.appInfo.packageName;
+ long contributedFileSize = getContributedMediaSize(pkg,
+ dialogInfo.allUsers ? null : dialogInfo.user);
+
+ boolean suggestToKeepAppData;
+ try {
+ PackageInfo pkgInfo = pm.getPackageInfo(pkg, 0);
+
+ suggestToKeepAppData = pkgInfo.applicationInfo.hasFragileUserData();
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Cannot check hasFragileUserData for " + pkg, e);
+ suggestToKeepAppData = false;
+ }
+
+ long appDataSize = 0;
+ if (suggestToKeepAppData) {
+ appDataSize = getAppDataSize(pkg, dialogInfo.allUsers ? null : dialogInfo.user);
+ }
+
+ if (contributedFileSize == 0 && appDataSize == 0) {
+ dialogBuilder.setMessage(messageBuilder.toString());
+ } else {
+ LayoutInflater inflater = getContext().getSystemService(LayoutInflater.class);
+ ViewGroup content = (ViewGroup) inflater.inflate(R.layout.uninstall_content_view, null);
+
+ ((TextView) content.requireViewById(R.id.message)).setText(messageBuilder.toString());
+
+ if (contributedFileSize != 0) {
+ mClearContributedFiles = content.requireViewById(R.id.clearContributedFiles);
+ mClearContributedFiles.setVisibility(View.VISIBLE);
+ mClearContributedFiles.setText(
+ getString(R.string.uninstall_remove_contributed_files,
+ formatFileSize(getContext(), contributedFileSize)));
+ }
+
+ if (appDataSize != 0) {
+ mKeepData = content.requireViewById(R.id.keepData);
+ mKeepData.setVisibility(View.VISIBLE);
+ mKeepData.setText(getString(R.string.uninstall_keep_data,
+ formatFileSize(getContext(), appDataSize)));
+ }
+
+ dialogBuilder.setView(content);
+ }
+
return dialogBuilder.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == Dialog.BUTTON_POSITIVE) {
- ((UninstallerActivity) getActivity()).startUninstallProgress();
+ ((UninstallerActivity) getActivity()).startUninstallProgress(
+ mClearContributedFiles != null && mClearContributedFiles.isChecked(),
+ mKeepData != null && mKeepData.isChecked());
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
index 828e5db..ac5fd76 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -99,7 +99,7 @@
public void onGuidedActionClicked(GuidedAction action) {
if (isAdded()) {
if (action.getId() == GuidedAction.ACTION_ID_OK) {
- ((UninstallerActivity) getActivity()).startUninstallProgress();
+ ((UninstallerActivity) getActivity()).startUninstallProgress(false, false);
getActivity().finish();
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 03eafc4..478ee54 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -51,6 +51,7 @@
<application
android:allowClearUserData="true"
android:label="@string/app_label"
+ android:icon="@drawable/ic_app_icon"
android:supportsRtl="true">
<service
diff --git a/packages/PrintSpooler/res/drawable/app_icon_foreground.xml b/packages/PrintSpooler/res/drawable/app_icon_foreground.xml
new file mode 100644
index 0000000..249e387
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/app_icon_foreground.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="25%"
+ android:insetRight="25%"
+ android:insetBottom="25%"
+ android:insetLeft="25%">
+
+ <vector
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19,8L5,8c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zM16,19L8,19v-5h8v5zM19,12c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,3L6,3v4h12L18,3z" />
+ </vector>
+</inset>
diff --git a/packages/PrintSpooler/res/drawable/ic_app_icon.xml b/packages/PrintSpooler/res/drawable/ic_app_icon.xml
new file mode 100644
index 0000000..82c18e0
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_app_icon.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@*android:color/accent_device_default_light"/>
+ <foreground android:drawable="@drawable/app_icon_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 0ccf13e..9e16f5e 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -107,7 +107,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:elevation="@dimen/preview_controls_elevation"
- android:tint="?android:attr/textColorPrimaryInverse"
+ android:tint="@android:color/white"
android:background="@drawable/print_button">
</ImageButton>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index cc17b25..0126e7e5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -18,6 +18,7 @@
"SettingsLibSettingsSpinner",
"SettingsLayoutPreference",
"ActionButtonsPreference",
+ "SettingsLibEntityHeaderWidgets",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/EntityHeaderWidgets/Android.bp b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
new file mode 100644
index 0000000..3ca4ecd
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
@@ -0,0 +1,14 @@
+android_library {
+ name: "SettingsLibEntityHeaderWidgets",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "SettingsLibAppPreference"
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
new file mode 100644
index 0000000..9f30eda
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_entities_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="24dp"
+ android:paddingEnd="8dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/header_title"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"
+ android:textAppearance="@style/AppEntitiesHeader.Text.HeaderTitle"/>
+
+ <LinearLayout
+ android:id="@+id/all_apps_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:gravity="center">
+
+ <include
+ android:id="@+id/app1_view"
+ layout="@layout/app_view"/>
+
+ <include
+ android:id="@+id/app2_view"
+ layout="@layout/app_view"/>
+
+ <include
+ android:id="@+id/app3_view"
+ layout="@layout/app_view"/>
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/header_details"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
new file mode 100644
index 0000000..fcafa31
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginEnd="16dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"
+ android:layout_marginBottom="12dp"/>
+
+ <TextView
+ android:id="@+id/app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/AppEntitiesHeader.Text.Title"/>
+
+ <TextView
+ android:id="@+id/app_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/AppEntitiesHeader.Text.Summary"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml b/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml
new file mode 100644
index 0000000..0eefd4b
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <style name="AppEntitiesHeader.Text"
+ parent="@android:style/TextAppearance.Material.Subhead">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="AppEntitiesHeader.Text.HeaderTitle">
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="AppEntitiesHeader.Text.Title">
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="AppEntitiesHeader.Text.Summary"
+ parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
new file mode 100644
index 0000000..8ccf89f
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * This is used to initialize view which was inflated
+ * from {@link R.xml.app_entities_header.xml}.
+ *
+ * <p>The view looks like below.
+ *
+ * <pre>
+ * --------------------------------------------------------------
+ * | Header title |
+ * --------------------------------------------------------------
+ * | App1 icon | App2 icon | App3 icon |
+ * | App1 title | App2 title | App3 title |
+ * | App1 summary | App2 summary | App3 summary |
+ * |-------------------------------------------------------------
+ * | Header details |
+ * --------------------------------------------------------------
+ * </pre>
+ *
+ * <p>How to use AppEntitiesHeaderController?
+ *
+ * <p>1. Add a {@link LayoutPreference} in layout XML file.
+ * <pre>
+ * <com.android.settingslib.widget.LayoutPreference
+ * android:key="app_entities_header"
+ * android:layout="@layout/app_entities_header"/>
+ * </pre>
+ *
+ * <p>2. Use AppEntitiesHeaderController to call below methods, then you can initialize
+ * view of <code>app_entities_header</code>.
+ *
+ * <pre>
+ *
+ * View headerView = ((LayoutPreference) screen.findPreference("app_entities_header"))
+ * .findViewById(R.id.app_entities_header);
+ *
+ * AppEntitiesHeaderController.newInstance(context, headerView)
+ * .setHeaderTitleRes(R.string.xxxxx)
+ * .setHeaderDetailsRes(R.string.xxxxx)
+ * .setHeaderDetailsClickListener(onClickListener)
+ * .setAppEntity(0, icon, "app title", "app summary")
+ * .setAppEntity(1, icon, "app title", "app summary")
+ * .setAppEntity(2, icon, "app title", "app summary")
+ * .apply();
+ * </pre>
+ */
+public class AppEntitiesHeaderController {
+
+ private static final String TAG = "AppEntitiesHeaderCtl";
+
+ @VisibleForTesting
+ static final int MAXIMUM_APPS = 3;
+
+ private final Context mContext;
+ private final TextView mHeaderTitleView;
+ private final Button mHeaderDetailsView;
+
+ private final AppEntity[] mAppEntities;
+ private final View[] mAppEntityViews;
+ private final ImageView[] mAppIconViews;
+ private final TextView[] mAppTitleViews;
+ private final TextView[] mAppSummaryViews;
+
+ private int mHeaderTitleRes;
+ private int mHeaderDetailsRes;
+ private View.OnClickListener mDetailsOnClickListener;
+
+ /**
+ * Creates a new instance of the controller.
+ *
+ * @param context the Context the view is running in
+ * @param appEntitiesHeaderView view was inflated from <code>app_entities_header</code>
+ */
+ public static AppEntitiesHeaderController newInstance(@NonNull Context context,
+ @NonNull View appEntitiesHeaderView) {
+ return new AppEntitiesHeaderController(context, appEntitiesHeaderView);
+ }
+
+ private AppEntitiesHeaderController(Context context, View appEntitiesHeaderView) {
+ mContext = context;
+ mHeaderTitleView = appEntitiesHeaderView.findViewById(R.id.header_title);
+ mHeaderDetailsView = appEntitiesHeaderView.findViewById(R.id.header_details);
+
+ mAppEntities = new AppEntity[MAXIMUM_APPS];
+ mAppIconViews = new ImageView[MAXIMUM_APPS];
+ mAppTitleViews = new TextView[MAXIMUM_APPS];
+ mAppSummaryViews = new TextView[MAXIMUM_APPS];
+
+ mAppEntityViews = new View[]{
+ appEntitiesHeaderView.findViewById(R.id.app1_view),
+ appEntitiesHeaderView.findViewById(R.id.app2_view),
+ appEntitiesHeaderView.findViewById(R.id.app3_view)
+ };
+
+ // Initialize view in advance, so we won't take too much time to do it when controller is
+ // binding view.
+ for (int index = 0; index < MAXIMUM_APPS; index++) {
+ final View appView = mAppEntityViews[index];
+ mAppIconViews[index] = (ImageView) appView.findViewById(R.id.app_icon);
+ mAppTitleViews[index] = (TextView) appView.findViewById(R.id.app_title);
+ mAppSummaryViews[index] = (TextView) appView.findViewById(R.id.app_summary);
+ }
+ }
+
+ /**
+ * Set the text resource for app entities header title.
+ */
+ public AppEntitiesHeaderController setHeaderTitleRes(@StringRes int titleRes) {
+ mHeaderTitleRes = titleRes;
+ return this;
+ }
+
+ /**
+ * Set the text resource for app entities header details.
+ */
+ public AppEntitiesHeaderController setHeaderDetailsRes(@StringRes int detailsRes) {
+ mHeaderDetailsRes = detailsRes;
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when header details view is clicked.
+ */
+ public AppEntitiesHeaderController setHeaderDetailsClickListener(
+ @Nullable View.OnClickListener clickListener) {
+ mDetailsOnClickListener = clickListener;
+ return this;
+ }
+
+ /**
+ * Set an app entity at a specified position view.
+ *
+ * @param index the index at which the specified view is to be inserted
+ * @param icon the icon of app entity
+ * @param titleRes the title of app entity
+ * @param summaryRes the summary of app entity
+ * @return this {@code AppEntitiesHeaderController} object
+ */
+ public AppEntitiesHeaderController setAppEntity(int index, @NonNull Drawable icon,
+ @Nullable CharSequence titleRes, @Nullable CharSequence summaryRes) {
+ final AppEntity appEntity = new AppEntity(icon, titleRes, summaryRes);
+ mAppEntities[index] = appEntity;
+ return this;
+ }
+
+ /**
+ * Remove an app entity at a specified position view.
+ *
+ * @param index the index at which the specified view is to be removed
+ * @return this {@code AppEntitiesHeaderController} object
+ */
+ public AppEntitiesHeaderController removeAppEntity(int index) {
+ mAppEntities[index] = null;
+ return this;
+ }
+
+ /**
+ * Clear all app entities in app entities header.
+ *
+ * @return this {@code AppEntitiesHeaderController} object
+ */
+ public AppEntitiesHeaderController clearAllAppEntities() {
+ for (int index = 0; index < MAXIMUM_APPS; index++) {
+ removeAppEntity(index);
+ }
+ return this;
+ }
+
+ /**
+ * Done mutating app entities header, rebinds everything.
+ */
+ public void apply() {
+ bindHeaderTitleView();
+ bindHeaderDetailsView();
+
+ // Rebind all apps view
+ for (int index = 0; index < MAXIMUM_APPS; index++) {
+ bindAppEntityView(index);
+ }
+ }
+
+ private void bindHeaderTitleView() {
+ CharSequence titleText = "";
+ try {
+ titleText = mContext.getText(mHeaderTitleRes);
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource of header title can't not be found!", e);
+ }
+ mHeaderTitleView.setText(titleText);
+ mHeaderTitleView.setVisibility(
+ TextUtils.isEmpty(titleText) ? View.GONE : View.VISIBLE);
+ }
+
+ private void bindHeaderDetailsView() {
+ CharSequence detailsText = "";
+ try {
+ detailsText = mContext.getText(mHeaderDetailsRes);
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource of header details can't not be found!", e);
+ }
+ mHeaderDetailsView.setText(detailsText);
+ mHeaderDetailsView.setVisibility(
+ TextUtils.isEmpty(detailsText) ? View.GONE : View.VISIBLE);
+ mHeaderDetailsView.setOnClickListener(mDetailsOnClickListener);
+ }
+
+ private void bindAppEntityView(int index) {
+ final AppEntity appEntity = mAppEntities[index];
+ mAppEntityViews[index].setVisibility(appEntity != null ? View.VISIBLE : View.GONE);
+
+ if (appEntity != null) {
+ mAppIconViews[index].setImageDrawable(appEntity.icon);
+
+ mAppTitleViews[index].setVisibility(
+ TextUtils.isEmpty(appEntity.title) ? View.INVISIBLE : View.VISIBLE);
+ mAppTitleViews[index].setText(appEntity.title);
+
+ mAppSummaryViews[index].setVisibility(
+ TextUtils.isEmpty(appEntity.summary) ? View.INVISIBLE : View.VISIBLE);
+ mAppSummaryViews[index].setText(appEntity.summary);
+ }
+ }
+
+ private static class AppEntity {
+ public final Drawable icon;
+ public final CharSequence title;
+ public final CharSequence summary;
+
+ AppEntity(Drawable appIcon, CharSequence appTitle, CharSequence appSummary) {
+ icon = appIcon;
+ title = appTitle;
+ summary = appSummary;
+ }
+ }
+}
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
index 7e65848..a046332 100644
--- a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/colorControlNormal">
+ android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FF000000"
android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 8ebb182..5b09b29 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -184,7 +184,7 @@
<string name="category_work" msgid="8699184680584175622">"कार्य"</string>
<string name="development_settings_title" msgid="215179176067683667">"डेव्हलपर पर्याय"</string>
<string name="development_settings_enable" msgid="542530994778109538">"डेव्हलपर पर्याय सुरू करा"</string>
- <string name="development_settings_summary" msgid="1815795401632854041">"अॅप विकासासाठी पर्याय सेट करा"</string>
+ <string name="development_settings_summary" msgid="1815795401632854041">"अॅप विकासासाठी पर्याय सेट करा"</string>
<string name="development_settings_not_available" msgid="4308569041701535607">"या वापरकर्त्यासाठी डेव्हलपर पर्याय उपलब्ध नाहीत"</string>
<string name="vpn_settings_not_available" msgid="956841430176985598">"या वापरकर्त्यासाठी VPN सेटिंग्ज उपलब्ध नाहीत"</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"या वापरकर्त्यासाठी टेदरिंग सेटिंग्ज उपलब्ध नाहीत"</string>
@@ -202,9 +202,9 @@
<string name="oem_unlock_enable_summary" msgid="4720281828891618376">"बूटलोडर अनलॉक करण्यासाठी अनुमती द्या"</string>
<string name="confirm_enable_oem_unlock_title" msgid="4802157344812385674">"OEM अनलॉक करण्यास अनुमती द्यायची?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"चेतावणी: हे सेटिंग चालू असताना या डिव्हाइस वर डिव्हाइस संरक्षण वैशिष्ट्ये काम करणार नाहीत."</string>
- <string name="mock_location_app" msgid="7966220972812881854">"बनावट स्थान अॅप निवडा"</string>
- <string name="mock_location_app_not_set" msgid="809543285495344223">"कोणताही बनावट स्थान अॅप सेट केला नाही"</string>
- <string name="mock_location_app_set" msgid="8966420655295102685">"बनावट स्थान अॅप: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="mock_location_app" msgid="7966220972812881854">"बनावट स्थान अॅप निवडा"</string>
+ <string name="mock_location_app_not_set" msgid="809543285495344223">"कोणताही बनावट स्थान अॅप सेट केला नाही"</string>
+ <string name="mock_location_app_set" msgid="8966420655295102685">"बनावट स्थान अॅप: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किंग"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"वायरलेस डिस्प्ले प्रमाणीकरण"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"वाय-फाय व्हर्बोझ लॉगिंग सुरू करा"</string>
@@ -261,11 +261,11 @@
<string name="bluetooth_show_devices_without_names_summary" msgid="2351196058115755520">"नावांशिवाय ब्लूटूथ डीव्हाइस (फक्त MAC पत्ते) दाखवले जातील"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="6031284410786545957">"रिमोट डिव्हाइसमध्ये सहन न होणारा मोठा आवाज किंवा नियंत्रणाचा अभाव यासारखी आवाजाची समस्या असल्यास ब्लूटूथ संपूर्ण आवाज वैशिष्ट्य बंद करते."</string>
<string name="enable_terminal_title" msgid="95572094356054120">"स्थानिक टर्मिनल"</string>
- <string name="enable_terminal_summary" msgid="67667852659359206">"स्थानिक शेल प्रवेश देणारा टर्मिनल अॅप सुरू करा"</string>
+ <string name="enable_terminal_summary" msgid="67667852659359206">"स्थानिक शेल प्रवेश देणारा टर्मिनल अॅप सुरू करा"</string>
<string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP तपासणी"</string>
<string name="hdcp_checking_dialog_title" msgid="5141305530923283">"HDCP तपासणी वर्तन सेट करा"</string>
<string name="debug_debugging_category" msgid="6781250159513471316">"डीबग करणे"</string>
- <string name="debug_app" msgid="8349591734751384446">"डीबग अॅप निवडा"</string>
+ <string name="debug_app" msgid="8349591734751384446">"डीबग अॅप निवडा"</string>
<string name="debug_app_not_set" msgid="718752499586403499">"कोणतेही डीबग अॅप्लिकेशन सेट नाही"</string>
<string name="debug_app_set" msgid="2063077997870280017">"अॅप्लिकेशन डीबग करत आहे: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="select_application" msgid="5156029161289091703">"अॅप्लिकेशन निवडा"</string>
@@ -315,7 +315,7 @@
<string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"वापरकर्त्याने प्रत्येक अॅक्टिव्हिटी सोडताच ती नष्ट करा"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"पार्श्वभूमी प्रक्रिया मर्यादा"</string>
<string name="show_all_anrs" msgid="4924885492787069007">"बॅकग्राउंड ANR दाखवा"</string>
- <string name="show_all_anrs_summary" msgid="6636514318275139826">"बॅकग्राउंड अॅप्ससाठी अॅप प्रतिसाद देत नाही दाखवते"</string>
+ <string name="show_all_anrs_summary" msgid="6636514318275139826">"बॅकग्राउंड अॅप्ससाठी अॅप प्रतिसाद देत नाही दाखवते"</string>
<string name="show_notification_channel_warnings" msgid="1399948193466922683">"सूचना चॅनेल चेतावण्या दाखवा"</string>
<string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"एखादे अॅप वैध चॅनेलशिवाय सूचना पोस्ट करते तेव्हा स्क्रीनवर चेतावणी देते"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"बाह्यवर अॅप्सना अनुमती देण्याची सक्ती करा"</string>
@@ -343,7 +343,7 @@
<string name="inactive_apps_title" msgid="9042996804461901648">"स्टँडबाय अॅप्स"</string>
<string name="inactive_app_inactive_summary" msgid="5091363706699855725">"निष्क्रिय. टॉगल करण्यासाठी टॅप करा."</string>
<string name="inactive_app_active_summary" msgid="4174921824958516106">"सक्रिय. टॉगल करण्यासाठी टॅप करा."</string>
- <string name="standby_bucket_summary" msgid="6567835350910684727">"अॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
+ <string name="standby_bucket_summary" msgid="6567835350910684727">"अॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="runningservices_settings_title" msgid="8097287939865165213">"चालू सेवा"</string>
<string name="runningservices_settings_summary" msgid="854608995821032748">"सध्या चालत असलेल्या सेवा पहा आणि नियंत्रित करा"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"वेबदृश्य अंमलबजावणी"</string>
@@ -422,7 +422,7 @@
<string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"सिस्टम भाषा वापरा"</string>
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> साठी सेटिंग्ज उघडण्यात अयशस्वी"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ही इनपुट पद्धत पासवर्ड आणि क्रेडिट कार्ड नंबर यासह, तुम्ही टाइप करता तो सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. ही <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अॅपवरून येते. ही इनपुट पद्धत वापरायची?"</string>
- <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टीप: रीबूट केल्यानंतर, तुम्ही तुमचा फोन अनलॉक करे पर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
+ <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टीप: रीबूट केल्यानंतर, तुम्ही तुमचा फोन अनलॉक करे पर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
<string name="ims_reg_title" msgid="7609782759207241443">"IMS नोंदणी स्थिती"</string>
<string name="ims_reg_status_registered" msgid="933003316932739188">"नोंदवलेले"</string>
<string name="ims_reg_status_not_registered" msgid="6529783773485229486">"नोंदवलेले नाही"</string>
@@ -449,6 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक वेळी विचारा"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"तुम्ही बंद करेपर्यंत"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"आत्ताच"</string>
- <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"अपडेट केलेले ग्राफिक ड्राइव्हर डेव्हलमेंटमध्ये वापरण्यासाठी अॅप निवडा"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"अपडेट केलेले ग्राफिक ड्राइव्हर डेव्हलमेंटमध्ये वापरण्यासाठी अॅप निवडा"</string>
<string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनचा स्पीकर"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index ae9c5f2..e810ba2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -40,7 +40,7 @@
<string name="connected_via_passpoint" msgid="2826205693803088747">"Tilkoblet via %1$s"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"Tilgjengelig via %1$s"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tilkoblet – ingen Internett-tilgang"</string>
- <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ingen Internett-tilkobling"</string>
+ <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ingen internettilkobling"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Pålogging kreves"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Tilgangspunktet er midlertidig fullt"</string>
<string name="connected_via_carrier" msgid="7583780074526041912">"Tilkoblet via %1$s"</string>
@@ -75,7 +75,7 @@
<string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internett-tilgang"</string>
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktdeling"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Bruk til kontaktdeling"</string>
- <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling av Internett-tilkobling"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling av internettilkobling"</string>
<string name="bluetooth_profile_map" msgid="1019763341565580450">"Tekstmeldinger"</string>
<string name="bluetooth_profile_sap" msgid="5764222021851283125">"Tilgang til SIM-kortet"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
@@ -90,7 +90,7 @@
<string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ikke koblet til tjener for filoverføring"</string>
<string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Koblet til inndataenhet"</string>
<string name="bluetooth_pan_user_profile_summary_connected" msgid="6436258151814414028">"Koblet til enhet for Internett-tilgang"</string>
- <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Deler lokal Internett-tilkobling med enhet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Deler lokal internettilkobling med enhet"</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Bruk for Internett-tilgang"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Bruk for kart"</string>
<string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Bruk for tilgang til SIM-kortet"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 2823149..842779d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -831,6 +831,10 @@
<!-- Toast message shown when setting a new local backup password fails due to the user not supplying the correct existing password. The phrasing here is deliberately quite general. [CHAR LIMIT=80] -->
<string name="local_backup_password_toast_validation_failure">Failure setting backup password</string>
+ <!-- [CHAR LIMIT=30] Location mode screen, temporary summary text to show as the status of a location
+ setting injected by an external app while the app is being queried for the actual value -->
+ <string name="loading_injected_setting_summary">Loading\u2026</string>
+
<!-- Name of each color mode for the display. [CHAR LIMIT=40] -->
<string-array name="color_mode_names">
<item>Vibrant (default)</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 1457fcf..74aaf3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -38,6 +38,7 @@
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
+import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
@@ -52,6 +53,9 @@
* support message dialog.
*/
public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
+
+ private static final String LOG_TAG = "RestrictedLockUtils";
+
/**
* @return drawables for displaying with settings that are locked by a device admin.
*/
@@ -305,6 +309,42 @@
return null;
}
+ /**
+ * @param userId user id of a managed profile.
+ * @return profile owner admin if cross profile calendar is disallowed.
+ */
+ public static EnforcedAdmin getCrossProfileCalendarEnforcingAdmin(Context context, int userId) {
+ final Context managedProfileContext = createPackageContextAsUser(
+ context, userId);
+ final DevicePolicyManager dpm = managedProfileContext.getSystemService(
+ DevicePolicyManager.class);
+ if (dpm == null) {
+ return null;
+ }
+ final EnforcedAdmin admin = getProfileOwner(context, userId);
+ if (admin == null) {
+ return null;
+ }
+ if (dpm.getCrossProfileCalendarPackages().isEmpty()) {
+ return admin;
+ }
+ return null;
+ }
+
+ /**
+ * @param userId user id of a managed profile.
+ * @return a context created from the given context for the given user, or null if it fails.
+ */
+ private static Context createPackageContextAsUser(Context context, int userId) {
+ try {
+ return context.createPackageContextAsUser(
+ context.getPackageName(), 0 /* flags */, UserHandle.of(userId));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Failed to create user context", e);
+ }
+ return null;
+ }
+
public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
String packageName, int userId) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
index ec8e956..2387b01 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
@@ -15,10 +15,9 @@
*/
package com.android.settingslib.applications;
-import android.annotation.NonNull;
import android.content.Context;
-import android.content.pm.permission.RuntimePermissionPresentationInfo;
-import android.content.pm.permission.RuntimePermissionPresenter;
+import android.permission.RuntimePermissionPresentationInfo;
+import android.permission.RuntimePermissionPresenter;
import java.text.Collator;
import java.util.ArrayList;
@@ -31,37 +30,33 @@
final PermissionsResultCallback callback) {
final RuntimePermissionPresenter presenter =
RuntimePermissionPresenter.getInstance(context);
- presenter.getAppPermissions(pkg, new RuntimePermissionPresenter.OnResultCallback() {
- @Override
- public void onGetAppPermissions(
- @NonNull List<RuntimePermissionPresentationInfo> permissions) {
- final int permissionCount = permissions.size();
+ presenter.getAppPermissions(pkg, permissions -> {
+ final int permissionCount = permissions.size();
- int grantedStandardCount = 0;
- int grantedAdditionalCount = 0;
- int requestedCount = 0;
- List<CharSequence> grantedStandardLabels = new ArrayList<>();
+ int grantedStandardCount = 0;
+ int grantedAdditionalCount = 0;
+ int requestedCount = 0;
+ List<CharSequence> grantedStandardLabels = new ArrayList<>();
- for (int i = 0; i < permissionCount; i++) {
- RuntimePermissionPresentationInfo permission = permissions.get(i);
- requestedCount++;
- if (permission.isGranted()) {
- if (permission.isStandard()) {
- grantedStandardLabels.add(permission.getLabel());
- grantedStandardCount++;
- } else {
- grantedAdditionalCount++;
- }
+ for (int i = 0; i < permissionCount; i++) {
+ RuntimePermissionPresentationInfo permission = permissions.get(i);
+ requestedCount++;
+ if (permission.isGranted()) {
+ if (permission.isStandard()) {
+ grantedStandardLabels.add(permission.getLabel());
+ grantedStandardCount++;
+ } else {
+ grantedAdditionalCount++;
}
}
-
- Collator collator = Collator.getInstance();
- collator.setStrength(Collator.PRIMARY);
- Collections.sort(grantedStandardLabels, collator);
-
- callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
- grantedAdditionalCount, grantedStandardLabels);
}
+
+ Collator collator = Collator.getInstance();
+ collator.setStrength(Collator.PRIMARY);
+ Collections.sort(grantedStandardLabels, collator);
+
+ callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
+ grantedAdditionalCount, grantedStandardLabels);
}, null);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9270d13..1bffff7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -71,15 +71,8 @@
boolean mJustDiscovered;
- private int mMessageRejectionCount;
-
private final Collection<Callback> mCallbacks = new ArrayList<>();
- // How many times user should reject the connection to make the choice persist.
- private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
-
- private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
-
/**
* Last time a bt profile auto-connect was attempted.
* If an ACTION_UUID intent comes in within
@@ -348,7 +341,6 @@
fetchActiveDevices();
migratePhonebookPermissionChoice();
migrateMessagePermissionChoice();
- fetchMessageRejectionCount();
dispatchAttributesChanged();
}
@@ -642,8 +634,6 @@
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
- mMessageRejectionCount = 0;
- saveMessageRejectionCount();
}
refresh();
@@ -797,34 +787,6 @@
editor.commit();
}
- /**
- * @return Whether this rejection should persist.
- */
- public boolean checkAndIncreaseMessageRejectionCount() {
- if (mMessageRejectionCount < MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST) {
- mMessageRejectionCount++;
- saveMessageRejectionCount();
- }
- return mMessageRejectionCount >= MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST;
- }
-
- private void fetchMessageRejectionCount() {
- SharedPreferences preference = mContext.getSharedPreferences(
- MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE);
- mMessageRejectionCount = preference.getInt(mDevice.getAddress(), 0);
- }
-
- private void saveMessageRejectionCount() {
- SharedPreferences.Editor editor = mContext.getSharedPreferences(
- MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE).edit();
- if (mMessageRejectionCount == 0) {
- editor.remove(mDevice.getAddress());
- } else {
- editor.putInt(mDevice.getAddress(), mMessageRejectionCount);
- }
- editor.commit();
- }
-
private void processPhonebookAccess() {
if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 67cfe6b..91892ab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -363,7 +363,8 @@
return null;
}
try {
- return provider.call(context.getPackageName(), method, uriString, null);
+ return provider.call(context.getPackageName(), uri.getAuthority(),
+ method, uriString, null);
} catch (RemoteException e) {
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
index 780fcba..74057be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -37,6 +37,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.IconDrawableFactory;
import android.util.Log;
@@ -44,11 +45,16 @@
import androidx.preference.Preference;
+import com.android.settingslib.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -102,7 +108,7 @@
public SettingsInjector(Context context) {
mContext = context;
mSettings = new HashSet<Setting>();
- mHandler = new StatusLoadingHandler();
+ mHandler = new StatusLoadingHandler(mSettings);
}
/**
@@ -165,7 +171,7 @@
Log.e(TAG, "Can't get ApplicationInfo for " + setting.packageName, e);
}
preference.setTitle(setting.title);
- preference.setSummary(null);
+ preference.setSummary(R.string.loading_injected_setting_summary);
preference.setIcon(appIcon);
preference.setOnPreferenceClickListener(new ServiceSettingClickedListener(setting));
}
@@ -180,6 +186,7 @@
final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
final List<UserHandle> profiles = um.getUserProfiles();
ArrayList<Preference> prefs = new ArrayList<>();
+ mSettings.clear();
for (UserHandle userHandle : profiles) {
if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
Iterable<InjectedSetting> settings = getSettings(userHandle);
@@ -363,31 +370,28 @@
* SettingInjectorService}, so to reduce memory pressure we don't want to load too many at
* once.
*/
- private final class StatusLoadingHandler extends Handler {
+ private static final class StatusLoadingHandler extends Handler {
+ /**
+ * References all the injected settings.
+ */
+ WeakReference<Set<Setting>> mAllSettings;
/**
* Settings whose status values need to be loaded. A set is used to prevent redundant loads.
*/
- private Set<Setting> mSettingsToLoad = new HashSet<Setting>();
+ private Deque<Setting> mSettingsToLoad = new ArrayDeque<Setting>();
/**
* Settings that are being loaded now and haven't timed out. In practice this should have
* zero or one elements.
*/
- private Set<Setting> mSettingsBeingLoaded = new HashSet<Setting>();
+ private Set<Setting> mSettingsBeingLoaded = new ArraySet<Setting>();
- /**
- * Settings that are being loaded but have timed out. If only one setting has timed out, we
- * will go ahead and start loading the next setting so that one slow load won't delay the
- * load of the other settings.
- */
- private Set<Setting> mTimedOutSettings = new HashSet<Setting>();
-
- private boolean mReloadRequested;
-
- private StatusLoadingHandler() {
+ public StatusLoadingHandler(Set<Setting> allSettings) {
super(Looper.getMainLooper());
+ mAllSettings = new WeakReference<>(allSettings);
}
+
@Override
public void handleMessage(Message msg) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -396,20 +400,24 @@
// Update state in response to message
switch (msg.what) {
- case WHAT_RELOAD:
- mReloadRequested = true;
+ case WHAT_RELOAD: {
+ final Set<Setting> allSettings = mAllSettings.get();
+ if (allSettings != null) {
+ // Reload requested, so must reload all settings
+ mSettingsToLoad.clear();
+ mSettingsToLoad.addAll(allSettings);
+ }
break;
+ }
case WHAT_RECEIVED_STATUS:
final Setting receivedSetting = (Setting) msg.obj;
receivedSetting.maybeLogElapsedTime();
mSettingsBeingLoaded.remove(receivedSetting);
- mTimedOutSettings.remove(receivedSetting);
removeMessages(WHAT_TIMEOUT, receivedSetting);
break;
case WHAT_TIMEOUT:
final Setting timedOutSetting = (Setting) msg.obj;
mSettingsBeingLoaded.remove(timedOutSetting);
- mTimedOutSettings.add(timedOutSetting);
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Timed out after " + timedOutSetting.getElapsedTime()
+ " millis trying to get status for: " + timedOutSetting);
@@ -421,37 +429,22 @@
// Decide whether to load additional settings based on the new state. Start by seeing
// if we have headroom to load another setting.
- if (mSettingsBeingLoaded.size() > 0 || mTimedOutSettings.size() > 1) {
+ if (mSettingsBeingLoaded.size() > 0) {
// Don't load any more settings until one of the pending settings has completed.
- // To reduce memory pressure, we want to be loading at most one setting (plus at
- // most one timed-out setting) at a time. This means we'll be responsible for
- // bringing in at most two services.
+ // To reduce memory pressure, we want to be loading at most one setting.
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "too many services already live for " + msg + ", " + this);
}
return;
}
- if (mReloadRequested && mSettingsToLoad.isEmpty() && mSettingsBeingLoaded.isEmpty()
- && mTimedOutSettings.isEmpty()) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "reloading because idle and reload requesteed " + msg + ", " + this);
- }
- // Reload requested, so must reload all settings
- mSettingsToLoad.addAll(mSettings);
- mReloadRequested = false;
- }
-
- // Remove the next setting to load from the queue, if any
- Iterator<Setting> iter = mSettingsToLoad.iterator();
- if (!iter.hasNext()) {
+ if (mSettingsToLoad.isEmpty()) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "nothing left to do for " + msg + ", " + this);
}
return;
}
- Setting setting = iter.next();
- iter.remove();
+ Setting setting = mSettingsToLoad.removeFirst();
// Request the status value
setting.startService();
@@ -473,21 +466,48 @@
return "StatusLoadingHandler{" +
"mSettingsToLoad=" + mSettingsToLoad +
", mSettingsBeingLoaded=" + mSettingsBeingLoaded +
- ", mTimedOutSettings=" + mTimedOutSettings +
- ", mReloadRequested=" + mReloadRequested +
'}';
}
}
+ private static class MessengerHandler extends Handler {
+ private WeakReference<Setting> mSettingRef;
+ private Handler mHandler;
+
+ public MessengerHandler(Setting setting, Handler handler) {
+ mSettingRef = new WeakReference(setting);
+ mHandler = handler;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final Setting setting = mSettingRef.get();
+ if (setting == null) {
+ return;
+ }
+ final Preference preference = setting.preference;
+ Bundle bundle = msg.getData();
+ boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true);
+ String summary = bundle.getString(SettingInjectorService.SUMMARY_KEY, null);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle);
+ }
+ preference.setSummary(summary);
+ preference.setEnabled(enabled);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(WHAT_RECEIVED_STATUS, setting));
+ }
+ }
+
/**
* Represents an injected setting and the corresponding preference.
*/
protected final class Setting {
-
public final InjectedSetting setting;
public final Preference preference;
public long startMillis;
+
public Setting(InjectedSetting setting, Preference preference) {
this.setting = setting;
this.preference = preference;
@@ -502,20 +522,6 @@
}
/**
- * Returns true if they both have the same {@link #setting} value. Ignores mutable
- * {@link #preference} and {@link #startMillis} so that it's safe to use in sets.
- */
- @Override
- public boolean equals(Object o) {
- return this == o || o instanceof Setting && setting.equals(((Setting) o).setting);
- }
-
- @Override
- public int hashCode() {
- return setting.hashCode();
- }
-
- /**
* Starts the service to fetch for the current status for the setting, and updates the
* preference when the service replies.
*/
@@ -529,20 +535,7 @@
}
return;
}
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Bundle bundle = msg.getData();
- boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true);
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle);
- }
- preference.setSummary(null);
- preference.setEnabled(enabled);
- mHandler.sendMessage(
- mHandler.obtainMessage(WHAT_RECEIVED_STATUS, Setting.this));
- }
- };
+ Handler handler = new MessengerHandler(this, mHandler);
Messenger messenger = new Messenger(handler);
Intent intent = setting.getServiceIntent();
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
deleted file mode 100644
index e9c5238..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.net;
-
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-import com.android.settingslib.AppItem;
-
-/**
- * Loader for historical chart data for both network and UID details.
- *
- * Deprecated in favor of {@link NetworkCycleChartDataLoader} and
- * {@link NetworkCycleDataForUidLoader}
- *
- * @deprecated
- */
-@Deprecated
-public class ChartDataLoaderCompat extends AsyncTaskLoader<ChartData> {
- private static final String KEY_TEMPLATE = "template";
- private static final String KEY_APP = "app";
- private static final String KEY_FIELDS = "fields";
-
- private final INetworkStatsSession mSession;
- private final Bundle mArgs;
-
- public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
- return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
- }
-
- public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
- final Bundle args = new Bundle();
- args.putParcelable(KEY_TEMPLATE, template);
- args.putParcelable(KEY_APP, app);
- args.putInt(KEY_FIELDS, fields);
- return args;
- }
-
- public ChartDataLoaderCompat(Context context, INetworkStatsSession session, Bundle args) {
- super(context);
- mSession = session;
- mArgs = args;
- }
-
- @Override
- protected void onStartLoading() {
- super.onStartLoading();
- forceLoad();
- }
-
- @Override
- public ChartData loadInBackground() {
- final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
- final AppItem app = mArgs.getParcelable(KEY_APP);
- final int fields = mArgs.getInt(KEY_FIELDS);
-
- try {
- return loadInBackground(template, app, fields);
- } catch (RemoteException e) {
- // since we can't do much without history, and we don't want to
- // leave with half-baked UI, we bail hard.
- throw new RuntimeException("problem reading network stats", e);
- }
- }
-
- private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
- throws RemoteException {
- final ChartData data = new ChartData();
- data.network = mSession.getHistoryForNetwork(template, fields);
-
- if (app != null) {
- // load stats for current uid and template
- final int size = app.uids.size();
- for (int i = 0; i < size; i++) {
- final int uid = app.uids.keyAt(i);
- data.detailDefault = collectHistoryForUid(
- template, uid, SET_DEFAULT, data.detailDefault);
- data.detailForeground = collectHistoryForUid(
- template, uid, SET_FOREGROUND, data.detailForeground);
- }
-
- if (size > 0) {
- data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
- data.detail.recordEntireHistory(data.detailDefault);
- data.detail.recordEntireHistory(data.detailForeground);
- } else {
- data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
- data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
- data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
- }
- }
-
- return data;
- }
-
- @Override
- protected void onStopLoading() {
- super.onStopLoading();
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- super.onReset();
- cancelLoad();
- }
-
- /**
- * Collect {@link NetworkStatsHistory} for the requested UID, combining with
- * an existing {@link NetworkStatsHistory} if provided.
- */
- private NetworkStatsHistory collectHistoryForUid(
- NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
- throws RemoteException {
- final NetworkStatsHistory history = mSession.getHistoryForUid(
- template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
-
- if (existing != null) {
- existing.recordEntireHistory(history);
- return existing;
- } else {
- return history;
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 183d485..180b77e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -32,30 +32,24 @@
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Range;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import java.time.ZonedDateTime;
-import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
public class DataUsageController {
private static final String TAG = "DataUsageController";
- @VisibleForTesting
- static final String DATA_USAGE_V2 = "settings_data_usage_v2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
@@ -95,21 +89,6 @@
* mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
}
- @VisibleForTesting
- @Deprecated
- INetworkStatsSession getSession() {
- if (mSession == null) {
- try {
- mSession = mStatsService.openSession();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to open stats session", e);
- } catch (RuntimeException e) {
- Log.w(TAG, "Failed to open stats session", e);
- }
- }
- return mSession;
- }
-
public void setCallback(Callback callback) {
mCallback = callback;
}
@@ -149,13 +128,7 @@
end = now;
start = now - DateUtils.WEEK_IN_MILLIS * 4;
}
- final long totalBytes;
- final long callStart = System.currentTimeMillis();
- if (FeatureFlagUtils.isEnabled(mContext, DATA_USAGE_V2)) {
- totalBytes = getUsageLevel(template, start, end);
- } else {
- totalBytes = getUsageLevel(template, start, end, now);
- }
+ final long totalBytes = getUsageLevel(template, start, end);
if (totalBytes < 0L) {
return warn("no entry data");
}
@@ -185,32 +158,7 @@
* retrieving the data.
*/
public long getHistoricalUsageLevel(NetworkTemplate template) {
- if (FeatureFlagUtils.isEnabled(mContext, DATA_USAGE_V2)) {
- return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
- } else {
- final long now = System.currentTimeMillis();
- return getUsageLevel(template, 0L /* start */, now /* end */, now);
- }
- }
-
- @Deprecated
- private long getUsageLevel(NetworkTemplate template, long start, long end, long now) {
- final INetworkStatsSession session = getSession();
- if (session != null) {
- try {
- final NetworkStatsHistory history =
- session.getHistoryForNetwork(template, FIELDS);
- final NetworkStatsHistory.Entry entry = history.getValues(
- start, end, System.currentTimeMillis() /* now */, null /* recycle */);
- if (entry != null) {
- return entry.rxBytes + entry.txBytes;
- }
- Log.w(TAG, "Failed to get data usage, no entry data");
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get data usage, remote call failed");
- }
- }
- return -1L;
+ return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
}
private long getUsageLevel(NetworkTemplate template, long start, long end) {
@@ -241,20 +189,6 @@
return null;
}
- @Deprecated
- private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
- return entry == null ? null : new StringBuilder("Entry[")
- .append("bucketDuration=").append(entry.bucketDuration)
- .append(",bucketStart=").append(entry.bucketStart)
- .append(",activeTime=").append(entry.activeTime)
- .append(",rxBytes=").append(entry.rxBytes)
- .append(",rxPackets=").append(entry.rxPackets)
- .append(",txBytes=").append(entry.txBytes)
- .append(",txPackets=").append(entry.txPackets)
- .append(",operations=").append(entry.operations)
- .append(']').toString();
- }
-
private static String statsBucketToString(Bucket bucket) {
return bucket == null ? null : new StringBuilder("Entry[")
.append("bucketDuration=").append(bucket.getEndTimeStamp() - bucket.getStartTimeStamp())
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
deleted file mode 100644
index 82bb011..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.net;
-
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-/**
- * Deprecated in favor of {@link NetworkStatsDetailLoader}
- *
- * @deprecated
- */
-@Deprecated
-public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
- private static final String KEY_TEMPLATE = "template";
- private static final String KEY_START = "start";
- private static final String KEY_END = "end";
-
- private final INetworkStatsSession mSession;
- private final Bundle mArgs;
-
- public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
- final Bundle args = new Bundle();
- args.putParcelable(KEY_TEMPLATE, template);
- args.putLong(KEY_START, start);
- args.putLong(KEY_END, end);
- return args;
- }
-
- public SummaryForAllUidLoaderCompat(Context context, INetworkStatsSession session,
- Bundle args) {
- super(context);
- mSession = session;
- mArgs = args;
- }
-
- @Override
- protected void onStartLoading() {
- super.onStartLoading();
- forceLoad();
- }
-
- @Override
- public NetworkStats loadInBackground() {
- final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
- final long start = mArgs.getLong(KEY_START);
- final long end = mArgs.getLong(KEY_END);
-
- try {
- return mSession.getSummaryForAllUid(template, start, end, false);
- } catch (RemoteException e) {
- return null;
- }
- }
-
- @Override
- protected void onStopLoading() {
- super.onStopLoading();
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- super.onReset();
- cancelLoad();
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index 72ed5e1..acf99a2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
@@ -35,12 +34,10 @@
import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
-import android.net.NetworkStatsHistory.Entry;
import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
-import android.util.FeatureFlagUtils;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +45,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class DataUsageControllerTest {
@@ -61,16 +57,18 @@
private TelephonyManager mTelephonyManager;
@Mock
private NetworkStatsManager mNetworkStatsManager;
-
+ @Mock
private Context mContext;
+
private DataUsageController mController;
private NetworkStatsHistory mNetworkStatsHistory;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mController = spy(new DataUsageController(mContext));
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+ mController = new DataUsageController(mContext);
mNetworkStatsHistory = spy(
new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
doReturn(mNetworkStatsHistory)
@@ -79,75 +77,25 @@
}
@Test
- public void getHistoricalUsageLevel_v1_noNetworkSession_shouldReturnNegative1() {
- FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
- doReturn(null).when(mController).getSession();
+ public void getHistoricalUsageLevel_shouldQuerySummaryForDevice() throws Exception {
- assertThat(mController.getHistoricalUsageLevel(null /* template */)).isEqualTo(-1L);
- }
-
- @Test
- public void getHistoriclUsageLevel_v1_noUsageData_shouldReturn0() {
- FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
- doReturn(mSession).when(mController).getSession();
-
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
- .isEqualTo(0L);
- }
-
- @Test
- public void getHistoricalUsageLevel_v1_hasUsageData_shouldReturnTotalUsage() {
- FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
- doReturn(mSession).when(mController).getSession();
- final long receivedBytes = 743823454L;
- final long transmittedBytes = 16574289L;
- final Entry entry = new Entry();
- entry.bucketStart = 1521583200000L;
- entry.rxBytes = receivedBytes;
- entry.txBytes = transmittedBytes;
- when(mNetworkStatsHistory.getValues(eq(0L), anyLong(), anyLong(), nullable(Entry.class)))
- .thenReturn(entry);
-
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
- .isEqualTo(receivedBytes + transmittedBytes);
- }
-
- @Test
- public void getHistoricalUsageLevel_v2_shouldQuerySummaryForDevice() throws Exception {
- final Context context = mock(Context.class);
- FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
- when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
- when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
- final DataUsageController controller = new DataUsageController(context);
-
- controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
+ mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
verify(mNetworkStatsManager).querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */);
}
@Test
- public void getHistoricalUsageLevel_v2NoUsageData_shouldReturn0() throws Exception {
- final Context context = mock(Context.class);
- FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
- when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
- when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+ public void getHistoricalUsageLevel_noUsageData_shouldReturn0() throws Exception {
when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */))
.thenReturn(mock(NetworkStats.Bucket.class));
- final DataUsageController controller = new DataUsageController(context);
-
- assertThat(controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
.isEqualTo(0L);
}
@Test
- public void getHistoricalUsageLevel_v2HasUsageData_shouldReturnTotalUsage()
- throws Exception {
- final Context context = mock(Context.class);
- FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
- when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
- when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+ public void getHistoricalUsageLevel_hasUsageData_shouldReturnTotalUsage() throws Exception {
final long receivedBytes = 743823454L;
final long transmittedBytes = 16574289L;
final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
@@ -155,9 +103,8 @@
when(bucket.getTxBytes()).thenReturn(transmittedBytes);
when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
- final DataUsageController controller = new DataUsageController(context);
- assertThat(controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
.isEqualTo(receivedBytes + transmittedBytes);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
index 4a8ef1e..924eb04 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
@@ -36,12 +36,12 @@
}
@Implementation
- public static int getCurrentUser() {
+ protected static int getCurrentUser() {
return sCurrentUserId;
}
@Implementation
- public boolean switchUser(int userId) {
+ protected boolean switchUser(int userId) {
mUserSwitchedTo = userId;
return true;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index cae74c8..906dba4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -32,7 +32,7 @@
private BluetoothProfile.ServiceListener mServiceListener;
@Implementation
- public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
+ protected boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
int profile) {
mServiceListener = listener;
return true;
@@ -43,7 +43,7 @@
}
@Implementation
- public List<Integer> getSupportedProfiles() {
+ protected List<Integer> getSupportedProfiles() {
return mSupportedProfiles;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
index 3e91641..d8fc861 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
@@ -34,7 +34,7 @@
}
@Implementation
- public static String getDefaultDialerApplication(Context context) {
+ protected static String getDefaultDialerApplication(Context context) {
return sDefaultDialer;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
index dd7b007..c8c4526 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
@@ -36,7 +36,7 @@
}
@Implementation
- public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
+ protected static ComponentName getDefaultSmsApplication(Context context, boolean update) {
return sDefaultSmsApplication;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index a81e395..c50d646 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -21,11 +21,8 @@
import android.content.pm.UserInfo;
import android.os.UserManager;
-import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
-import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@@ -56,5 +53,4 @@
protected List<UserInfo> getProfiles(@UserIdInt int userHandle) {
return getProfiles();
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java
deleted file mode 100644
index 3455765..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.testutils.shadow;
-
-import static org.robolectric.shadow.api.Shadow.directlyOn;
-
-import com.android.internal.util.XmlUtils;
-
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.util.ReflectionHelpers;
-
-@Implements(XmlUtils.class)
-public class ShadowXmlUtils {
-
- @Implementation
- public static final int convertValueToInt(CharSequence charSeq, int defaultValue) {
- final Class<?> xmlUtilsClass = ReflectionHelpers.loadClass(
- Robolectric.class.getClassLoader(), "com.android.internal.util.XmlUtils");
- try {
- return directlyOn(xmlUtilsClass, "convertValueToInt",
- ReflectionHelpers.ClassParameter.from(CharSequence.class, charSeq),
- ReflectionHelpers.ClassParameter.from(int.class, new Integer(defaultValue)));
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
index 9a169d2..83cc39a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -78,8 +78,8 @@
@Test
public void getForegroundUserInfo() {
ShadowActivityManager.setCurrentUser(17);
- when(mUserManager.getUserInfo(ShadowActivityManager.getCurrentUser()))
- .thenReturn(createUserInfoForId(ShadowActivityManager.getCurrentUser()));
+ when(mUserManager.getUserInfo(ActivityManager.getCurrentUser()))
+ .thenReturn(createUserInfoForId(ActivityManager.getCurrentUser()));
assertThat(mHelper.getForegroundUserInfo().id).isEqualTo(17);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
index 88fef08..97de7ef 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -18,9 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
new file mode 100644
index 0000000..c3bc8da
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppEntitiesHeaderControllerTest {
+
+ private static final CharSequence TITLE = "APP_TITLE";
+ private static final CharSequence SUMMARY = "APP_SUMMARY";
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ private Context mContext;
+ private Drawable mIcon;
+ private View mAppEntitiesHeaderView;
+ private AppEntitiesHeaderController mController;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mAppEntitiesHeaderView = LayoutInflater.from(mContext).inflate(
+ R.layout.app_entities_header, null /* root */);
+ mIcon = mContext.getDrawable(R.drawable.ic_menu);
+ mController = AppEntitiesHeaderController.newInstance(mContext,
+ mAppEntitiesHeaderView);
+ }
+
+ @Test
+ public void assert_amountOfMaximumAppsAreThree() {
+ assertThat(AppEntitiesHeaderController.MAXIMUM_APPS).isEqualTo(3);
+ }
+
+ @Test
+ public void setHeaderTitleRes_setTextRes_shouldSetToTitleView() {
+ mController.setHeaderTitleRes(R.string.expand_button_title).apply();
+ final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_title);
+
+ assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+ }
+
+ @Test
+ public void setHeaderDetailsRes_setTextRes_shouldSetToDetailsView() {
+ mController.setHeaderDetailsRes(R.string.expand_button_title).apply();
+ final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+ assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+ }
+
+ @Test
+ public void setHeaderDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
+ mController.setHeaderDetailsClickListener(v -> {
+ }).apply();
+ final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+ assertThat(view.hasOnClickListeners()).isTrue();
+ }
+
+ @Test
+ public void setAppEntity_indexLessThanZero_shouldThrowArrayIndexOutOfBoundsException() {
+ thrown.expect(ArrayIndexOutOfBoundsException.class);
+
+ mController.setAppEntity(-1, mIcon, TITLE, SUMMARY);
+ }
+
+ @Test
+ public void asetAppEntity_indexGreaterThanMaximum_shouldThrowArrayIndexOutOfBoundsException() {
+ thrown.expect(ArrayIndexOutOfBoundsException.class);
+
+ mController.setAppEntity(AppEntitiesHeaderController.MAXIMUM_APPS + 1, mIcon, TITLE,
+ SUMMARY);
+ }
+
+ @Test
+ public void setAppEntity_addAppToIndex0_shouldShowAppView1() {
+ mController.setAppEntity(0, mIcon, TITLE, SUMMARY).apply();
+ final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+ final ImageView appIconView = app1View.findViewById(R.id.app_icon);
+ final TextView appTitle = app1View.findViewById(R.id.app_title);
+ final TextView appSummary = app1View.findViewById(R.id.app_summary);
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(appIconView.getDrawable()).isNotNull();
+ assertThat(appTitle.getText()).isEqualTo(TITLE);
+ assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void setAppEntity_addAppToIndex1_shouldShowAppView2() {
+ mController.setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
+ final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+ final ImageView appIconView = app2View.findViewById(R.id.app_icon);
+ final TextView appTitle = app2View.findViewById(R.id.app_title);
+ final TextView appSummary = app2View.findViewById(R.id.app_summary);
+
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(appIconView.getDrawable()).isNotNull();
+ assertThat(appTitle.getText()).isEqualTo(TITLE);
+ assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void setAppEntity_addAppToIndex2_shouldShowAppView3() {
+ mController.setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
+ final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
+ final ImageView appIconView = app3View.findViewById(R.id.app_icon);
+ final TextView appTitle = app3View.findViewById(R.id.app_title);
+ final TextView appSummary = app3View.findViewById(R.id.app_summary);
+
+ assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(appIconView.getDrawable()).isNotNull();
+ assertThat(appTitle.getText()).isEqualTo(TITLE);
+ assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void removeAppEntity_removeIndex0_shouldNotShowAppView1() {
+ mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
+ .setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
+ final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+ final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mController.removeAppEntity(0).apply();
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.GONE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void clearAllAppEntities_shouldNotShowAllAppViews() {
+ mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
+ .setAppEntity(1, mIcon, TITLE, SUMMARY)
+ .setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
+ final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+ final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+ final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mController.clearAllAppEntities().apply();
+ assertThat(app1View.getVisibility()).isEqualTo(View.GONE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.GONE);
+ assertThat(app3View.getVisibility()).isEqualTo(View.GONE);
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
new file mode 100644
index 0000000..3520918
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.ActivityManager;
+import android.content.IContentProvider;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.provider.Settings;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Receives shell commands from the command line related to device config flags, and dispatches them
+ * to the SettingsProvider.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DeviceConfigService extends Binder {
+ /**
+ * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+ * API.
+ */
+ private static final Uri CONFIG_CONTENT_URI =
+ Uri.parse("content://" + Settings.AUTHORITY + "/config");
+
+ final SettingsProvider mProvider;
+
+ public DeviceConfigService(SettingsProvider provider) {
+ mProvider = provider;
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ (new MyShellCommand(mProvider)).exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ static final class MyShellCommand extends ShellCommand {
+ final SettingsProvider mProvider;
+
+ enum CommandVerb {
+ UNSPECIFIED,
+ GET,
+ PUT,
+ DELETE,
+ LIST,
+ RESET,
+ }
+
+ MyShellCommand(SettingsProvider provider) {
+ mProvider = provider;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
+ onHelp();
+ return -1;
+ }
+
+ final PrintWriter perr = getErrPrintWriter();
+ boolean isValid = false;
+ CommandVerb verb;
+ if ("get".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.GET;
+ } else if ("put".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.PUT;
+ } else if ("delete".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.DELETE;
+ } else if ("list".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.LIST;
+ if (peekNextArg() == null) {
+ isValid = true;
+ }
+ } else if ("reset".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.RESET;
+ } else {
+ // invalid
+ perr.println("Invalid command: " + cmd);
+ return -1;
+ }
+
+ int resetMode = -1;
+ boolean makeDefault = false;
+ String namespace = null;
+ String key = null;
+ String value = null;
+ String arg = null;
+ while ((arg = getNextArg()) != null) {
+ if (verb == CommandVerb.RESET) {
+ if (resetMode == -1) {
+ if ("untrusted_defaults".equalsIgnoreCase(arg)) {
+ resetMode = Settings.RESET_MODE_UNTRUSTED_DEFAULTS;
+ } else if ("untrusted_clear".equalsIgnoreCase(arg)) {
+ resetMode = Settings.RESET_MODE_UNTRUSTED_CHANGES;
+ } else if ("trusted_defaults".equalsIgnoreCase(arg)) {
+ resetMode = Settings.RESET_MODE_TRUSTED_DEFAULTS;
+ } else {
+ // invalid
+ perr.println("Invalid reset mode: " + arg);
+ return -1;
+ }
+ if (peekNextArg() == null) {
+ isValid = true;
+ }
+ } else {
+ namespace = arg;
+ if (peekNextArg() == null) {
+ isValid = true;
+ } else {
+ // invalid
+ perr.println("Too many arguments");
+ return -1;
+ }
+ }
+ } else if (namespace == null) {
+ namespace = arg;
+ if (verb == CommandVerb.LIST) {
+ if (peekNextArg() == null) {
+ isValid = true;
+ } else {
+ // invalid
+ perr.println("Too many arguments");
+ return -1;
+ }
+ }
+ } else if (key == null) {
+ key = arg;
+ if ((verb == CommandVerb.GET || verb == CommandVerb.DELETE)) {
+ if (peekNextArg() == null) {
+ isValid = true;
+ } else {
+ // invalid
+ perr.println("Too many arguments");
+ return -1;
+ }
+ }
+ } else if (value == null) {
+ value = arg;
+ if (verb == CommandVerb.PUT && peekNextArg() == null) {
+ isValid = true;
+ }
+ } else if ("default".equalsIgnoreCase(arg)) {
+ makeDefault = true;
+ if (verb == CommandVerb.PUT && peekNextArg() == null) {
+ isValid = true;
+ } else {
+ // invalid
+ perr.println("Too many arguments");
+ return -1;
+ }
+ }
+ }
+
+ if (!isValid) {
+ perr.println("Bad arguments");
+ return -1;
+ }
+
+ final IContentProvider iprovider = mProvider.getIContentProvider();
+ final PrintWriter pout = getOutPrintWriter();
+ switch (verb) {
+ case GET:
+ pout.println(get(iprovider, namespace, key));
+ break;
+ case PUT:
+ put(iprovider, namespace, key, value, makeDefault);
+ break;
+ case DELETE:
+ pout.println(delete(iprovider, namespace, key)
+ ? "Successfully deleted " + key + " from " + namespace
+ : "Failed to delete " + key + " from " + namespace);
+ break;
+ case LIST:
+ for (String line : list(iprovider, namespace)) {
+ pout.println(line);
+ }
+ break;
+ case RESET:
+ reset(iprovider, resetMode, namespace);
+ break;
+ default:
+ perr.println("Unspecified command");
+ return -1;
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Device Config (device_config) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" get NAMESPACE KEY");
+ pw.println(" Retrieve the current value of KEY from the given NAMESPACE.");
+ pw.println(" put NAMESPACE KEY VALUE [default]");
+ pw.println(" Change the contents of KEY to VALUE for the given NAMESPACE.");
+ pw.println(" {default} to set as the default value.");
+ pw.println(" delete NAMESPACE KEY");
+ pw.println(" Delete the entry for KEY for the given NAMESPACE.");
+ pw.println(" list [NAMESPACE]");
+ pw.println(" Print all keys and values defined, optionally for the given "
+ + "NAMESPACE.");
+ pw.println(" reset RESET_MODE [NAMESPACE]");
+ pw.println(" Reset all flag values, optionally for a NAMESPACE, according to "
+ + "RESET_MODE.");
+ pw.println(" RESET_MODE is one of {untrusted_defaults, untrusted_clear, "
+ + "trusted_defaults}");
+ pw.println(" NAMESPACE limits which flags are reset if provided, otherwise all "
+ + "flags are reset");
+ }
+
+ private String get(IContentProvider provider, String namespace, String key) {
+ String compositeKey = namespace + "/" + key;
+ String result = null;
+ try {
+ Bundle args = new Bundle();
+ args.putInt(Settings.CALL_METHOD_USER_KEY,
+ ActivityManager.getService().getCurrentUser().id);
+ Bundle b = provider.call(resolveCallingPackage(), Settings.CALL_METHOD_GET_CONFIG,
+ compositeKey, args);
+ if (b != null) {
+ result = b.getPairValue();
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed in IPC", e);
+ }
+ return result;
+ }
+
+ private void put(IContentProvider provider, String namespace, String key, String value,
+ boolean makeDefault) {
+ String compositeKey = namespace + "/" + key;
+
+ try {
+ Bundle args = new Bundle();
+ args.putString(Settings.NameValueTable.VALUE, value);
+ args.putInt(Settings.CALL_METHOD_USER_KEY,
+ ActivityManager.getService().getCurrentUser().id);
+ if (makeDefault) {
+ args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
+ }
+ provider.call(resolveCallingPackage(), Settings.CALL_METHOD_PUT_CONFIG,
+ compositeKey, args);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed in IPC", e);
+ }
+ }
+
+ private boolean delete(IContentProvider provider, String namespace, String key) {
+ String compositeKey = namespace + "/" + key;
+ boolean success;
+
+ try {
+ Bundle args = new Bundle();
+ args.putInt(Settings.CALL_METHOD_USER_KEY,
+ ActivityManager.getService().getCurrentUser().id);
+ Bundle b = provider.call(resolveCallingPackage(),
+ Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
+ success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed in IPC", e);
+ }
+ return success;
+ }
+
+ private List<String> list(IContentProvider provider, @Nullable String namespace) {
+ final ArrayList<String> lines = new ArrayList<>();
+
+ try {
+ Bundle args = new Bundle();
+ args.putInt(Settings.CALL_METHOD_USER_KEY,
+ ActivityManager.getService().getCurrentUser().id);
+ if (namespace != null) {
+ args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
+ }
+ Bundle b = provider.call(resolveCallingPackage(),
+ Settings.CALL_METHOD_LIST_CONFIG, null, args);
+ if (b != null) {
+ Map<String, String> flagsToValues =
+ (HashMap) b.getSerializable(Settings.NameValueTable.VALUE);
+ for (String key : flagsToValues.keySet()) {
+ lines.add(key + "=" + flagsToValues.get(key));
+ }
+ }
+
+ Collections.sort(lines);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed in IPC", e);
+ }
+ return lines;
+ }
+
+ private void reset(IContentProvider provider, int resetMode, @Nullable String namespace) {
+ try {
+ Bundle args = new Bundle();
+ args.putInt(Settings.CALL_METHOD_USER_KEY,
+ ActivityManager.getService().getCurrentUser().id);
+ args.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
+ args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
+ provider.call(
+ resolveCallingPackage(), Settings.CALL_METHOD_RESET_CONFIG, null, args);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed in IPC", e);
+ }
+ }
+
+ private static String resolveCallingPackage() {
+ switch (Binder.getCallingUid()) {
+ case Process.ROOT_UID: {
+ return "root";
+ }
+
+ case Process.SHELL_UID: {
+ return "com.android.shell";
+ }
+
+ default: {
+ return null;
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 56b768f..e3d3d81 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -208,9 +208,14 @@
GlobalSettingsProto.Autofill.MAX_VISIBLE_DATASETS);
p.end(autofillToken);
+ final long backupToken = p.start(GlobalSettingsProto.BACKUP);
dumpSetting(s, p,
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
- GlobalSettingsProto.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+ GlobalSettingsProto.Backup.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+ dumpSetting(s, p,
+ Settings.Global.BACKUP_MULTI_USER_ENABLED,
+ GlobalSettingsProto.Backup.BACKUP_MULTI_USER_ENABLED);
+ p.end(backupToken);
final long batteryToken = p.start(GlobalSettingsProto.BATTERY);
dumpSetting(s, p,
@@ -418,6 +423,12 @@
Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
GlobalSettingsProto.CONTACTS_DATABASE_WAL_ENABLED);
+ final long contentCaptureToken = p.start(GlobalSettingsProto.CONTENT_CAPTURE);
+ dumpSetting(s, p,
+ Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED,
+ GlobalSettingsProto.ContentCapture.SERVICE_EXPLICITLY_ENABLED);
+ p.end(contentCaptureToken);
+
final long dataToken = p.start(GlobalSettingsProto.DATA);
// Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA intentionally excluded.
dumpSetting(s, p,
@@ -679,8 +690,14 @@
Settings.Global.GPU_DEBUG_LAYERS,
GlobalSettingsProto.Gpu.DEBUG_LAYERS);
dumpSetting(s, p,
- Settings.Global.ANGLE_ENABLED_APP,
- GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP);
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+ GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_ALL_ANGLE);
+ dumpSetting(s, p,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
+ GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_PKGS);
+ dumpSetting(s, p,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+ GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
dumpSetting(s, p,
Settings.Global.GPU_DEBUG_LAYER_APP,
GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
@@ -998,6 +1015,9 @@
dumpSetting(s, p,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
GlobalSettingsProto.Notification.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS);
+ dumpSetting(s, p,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ GlobalSettingsProto.Notification.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS);
p.end(notificationToken);
dumpSetting(s, p,
@@ -1306,6 +1326,9 @@
dumpSetting(s, p,
Settings.Global.WARNING_TEMPERATURE,
GlobalSettingsProto.TemperatureWarning.WARNING_TEMPERATURE_LEVEL);
+ dumpSetting(s, p,
+ Settings.Global.USB_ALARM_TEMPERATURE,
+ GlobalSettingsProto.TemperatureWarning.USB_ALARM_TEMPERATURE_LEVEL);
p.end(tempWarningToken);
final long tetherToken = p.start(GlobalSettingsProto.TETHER);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 00ea45c..b071355 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -94,6 +94,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -147,6 +148,7 @@
private static final String TABLE_SYSTEM = "system";
private static final String TABLE_SECURE = "secure";
private static final String TABLE_GLOBAL = "global";
+ private static final String TABLE_CONFIG = "config";
// Old tables no longer exist.
private static final String TABLE_FAVORITES = "favorites";
@@ -333,6 +335,7 @@
startWatchingUserRestrictionChanges();
});
ServiceManager.addService("settings", new SettingsService(this));
+ ServiceManager.addService("device_config", new DeviceConfigService(this));
return true;
}
@@ -414,9 +417,8 @@
case Settings.CALL_METHOD_PUT_CONFIG: {
String value = getSettingValue(args);
- String tag = getSettingTag(args);
final boolean makeDefault = getSettingMakeDefault(args);
- insertConfigSetting(name, value, tag, makeDefault, requestingUserId, false);
+ insertConfigSetting(name, value, makeDefault);
break;
}
@@ -444,8 +446,8 @@
case Settings.CALL_METHOD_RESET_CONFIG: {
final int mode = getResetModeEnforcingPermission(args);
- String tag = getSettingTag(args);
- resetConfigSetting(requestingUserId, mode, tag);
+ String prefix = getSettingPrefix(args);
+ resetConfigSetting(mode, prefix);
break;
}
@@ -463,15 +465,8 @@
break;
}
- case Settings.CALL_METHOD_DELETE_SYSTEM: {
- int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
- Bundle result = new Bundle();
- result.putInt(RESULT_ROWS_DELETED, rows);
- return result;
- }
-
- case Settings.CALL_METHOD_DELETE_SECURE: {
- int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
+ case Settings.CALL_METHOD_DELETE_CONFIG: {
+ int rows = deleteConfigSetting(name) ? 1 : 0;
Bundle result = new Bundle();
result.putInt(RESULT_ROWS_DELETED, rows);
return result;
@@ -484,10 +479,32 @@
return result;
}
- case Settings.CALL_METHOD_LIST_SYSTEM: {
+ case Settings.CALL_METHOD_DELETE_SECURE: {
+ int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_DELETE_SYSTEM: {
+ int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_LIST_CONFIG: {
+ String prefix = getSettingPrefix(args);
+ Bundle result = new Bundle();
+ result.putSerializable(
+ Settings.NameValueTable.VALUE, (HashMap) getAllConfigFlags(prefix));
+ return result;
+ }
+
+ case Settings.CALL_METHOD_LIST_GLOBAL: {
Bundle result = new Bundle();
result.putStringArrayList(RESULT_SETTINGS_LIST,
- buildSettingsList(getAllSystemSettings(requestingUserId, null)));
+ buildSettingsList(getAllGlobalSettings(null)));
return result;
}
@@ -498,10 +515,10 @@
return result;
}
- case Settings.CALL_METHOD_LIST_GLOBAL: {
+ case Settings.CALL_METHOD_LIST_SYSTEM: {
Bundle result = new Bundle();
result.putStringArrayList(RESULT_SETTINGS_LIST,
- buildSettingsList(getAllGlobalSettings(null)));
+ buildSettingsList(getAllSystemSettings(requestingUserId, null)));
return result;
}
@@ -1050,47 +1067,53 @@
}
}
- private boolean insertConfigSetting(String name, String value, String tag,
- boolean makeDefault, int requestingUserId, boolean forceNotify) {
+ private boolean insertConfigSetting(String name, String value, boolean makeDefault) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertConfigSetting(" + name + ", " + value + ", "
- + ", " + tag + ", " + makeDefault + ", " + requestingUserId
- + ", " + forceNotify + ")");
+ + makeDefault + ")");
}
- return mutateConfigSetting(name, value, tag, makeDefault, requestingUserId,
- MUTATION_OPERATION_INSERT, forceNotify, 0);
+ return mutateConfigSetting(name, value, null, makeDefault,
+ MUTATION_OPERATION_INSERT, 0);
}
- private void resetConfigSetting(int requestingUserId, int mode, String tag) {
+ private boolean deleteConfigSetting(String name) {
if (DEBUG) {
- Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
- + mode + ", " + tag + ")");
+ Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ")");
}
- mutateConfigSetting(null, null, tag, false, requestingUserId,
- MUTATION_OPERATION_RESET, false, mode);
+ return mutateConfigSetting(name, null, null, false,
+ MUTATION_OPERATION_DELETE, 0);
}
- private boolean mutateConfigSetting(String name, String value, String tag,
- boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
- int mode) {
+ private void resetConfigSetting(int mode, String prefix) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "resetConfigSetting(" + mode + ", " + prefix + ")");
+ }
+ mutateConfigSetting(null, null, prefix, false,
+ MUTATION_OPERATION_RESET, mode);
+ }
+
+ private boolean mutateConfigSetting(String name, String value, String prefix,
+ boolean makeDefault, int operation, int mode) {
// TODO(b/117663715): check the new permission when it's added.
// enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
- // Resolve the userId on whose behalf the call is made.
- final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
-
// Perform the mutation.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
- UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify, null);
+ UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
+ getCallingPackage(), false, null);
+ }
+
+ case MUTATION_OPERATION_DELETE: {
+ return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM, name, false, null);
}
case MUTATION_OPERATION_RESET: {
mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
- UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag);
+ UserHandle.USER_SYSTEM, getCallingPackage(), mode, null, prefix);
} return true;
}
}
@@ -1098,6 +1121,34 @@
return false;
}
+ private Map<String, String> getAllConfigFlags(@Nullable String prefix) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
+ }
+
+ synchronized (mLock) {
+ // Get the settings.
+ SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+
+ List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM);
+
+ final int nameCount = names.size();
+ Map<String, String> flagsToValues = new HashMap<>(names.size());
+
+ for (int i = 0; i < nameCount; i++) {
+ String name = names.get(i);
+ Setting setting = settingsState.getSettingLocked(name);
+ if (prefix == null || setting.getName().startsWith(prefix)) {
+ flagsToValues.put(setting.getName(), setting.getValue());
+ }
+ }
+
+ return flagsToValues;
+ }
+ }
+
private Cursor getAllGlobalSettings(String[] projection) {
if (DEBUG) {
Slog.v(LOG_TAG, "getAllGlobalSettings()");
@@ -2085,6 +2136,13 @@
return (args != null) ? args.getString(Settings.CALL_METHOD_TAG_KEY) : null;
}
+ private static String getSettingPrefix(Bundle args) {
+ String prefix = (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
+ // Append '/' to ensure we only match properties with this exact prefix.
+ // i.e. "foo" should match "foo/property" but not "foobar/property"
+ return prefix != null ? prefix + "/" : null;
+ }
+
private static boolean getSettingMakeDefault(Bundle args) {
return (args != null) && args.getBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY);
}
@@ -2568,13 +2626,20 @@
public boolean insertSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, String packageName, boolean forceNotify,
Set<String> criticalSettings) {
+ return insertSettingLocked(type, userId, name, value, tag, makeDefault, false,
+ packageName, forceNotify, criticalSettings);
+ }
+
+ public boolean insertSettingLocked(int type, int userId, String name, String value,
+ String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
+ boolean forceNotify, Set<String> criticalSettings) {
final int key = makeKey(type, userId);
boolean success = false;
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
success = settingsState.insertSettingLocked(name, value,
- tag, makeDefault, packageName);
+ tag, makeDefault, forceNonSystemPackage, packageName);
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
@@ -2644,6 +2709,11 @@
public void resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
+ resetSettingsLocked(type, userId, packageName, mode, tag, null);
+ }
+
+ public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+ String tag, @Nullable String prefix) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState == null) {
@@ -2656,7 +2726,8 @@
boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (packageName.equals(setting.getPackageName())) {
- if (tag != null && !tag.equals(setting.getTag())) {
+ if ((tag != null && !tag.equals(setting.getTag()))
+ || (prefix != null && !setting.getName().startsWith(prefix))) {
continue;
}
if (settingsState.resetSettingLocked(name)) {
@@ -2676,6 +2747,9 @@
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
+ if (prefix != null && !setting.getName().startsWith(prefix)) {
+ continue;
+ }
if (settingsState.resetSettingLocked(name)) {
someSettingChanged = true;
notifyForSettingsChange(key, name);
@@ -2693,6 +2767,9 @@
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
+ if (prefix != null && !setting.getName().startsWith(prefix)) {
+ continue;
+ }
if (setting.isDefaultFromSystem()) {
if (settingsState.resetSettingLocked(name)) {
someSettingChanged = true;
@@ -2713,6 +2790,9 @@
for (String name : settingsState.getSettingNamesLocked()) {
Setting setting = settingsState.getSettingLocked(name);
boolean someSettingChanged = false;
+ if (prefix != null && !setting.getName().startsWith(prefix)) {
+ continue;
+ }
if (setting.isDefaultFromSystem()) {
if (settingsState.resetSettingLocked(name)) {
someSettingChanged = true;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 13537c4..36360a3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -105,7 +105,7 @@
RESET,
}
- int mUser = -1; // unspecified
+ int mUser = UserHandle.USER_NULL;
CommandVerb mVerb = CommandVerb.UNSPECIFIED;
String mTable = null;
String mKey = null;
@@ -132,15 +132,15 @@
String arg = cmd;
do {
if ("--user".equals(arg)) {
- if (mUser != -1) {
- // --user specified more than once; invalid
+ if (mUser != UserHandle.USER_NULL) {
+ perr.println("Invalid user: --user specified more than once");
break;
}
- arg = getNextArgRequired();
- if ("current".equals(arg) || "cur".equals(arg)) {
- mUser = UserHandle.USER_CURRENT;
- } else {
- mUser = Integer.parseInt(arg);
+ mUser = UserHandle.parseUserArg(getNextArgRequired());
+
+ if (mUser == UserHandle.USER_ALL) {
+ perr.println("Invalid user: all");
+ return -1;
}
} else if (mVerb == CommandVerb.UNSPECIFIED) {
if ("get".equalsIgnoreCase(arg)) {
@@ -254,16 +254,13 @@
return -1;
}
- if (mUser == UserHandle.USER_CURRENT) {
+ if (mUser == UserHandle.USER_NULL || mUser == UserHandle.USER_CURRENT) {
try {
mUser = ActivityManager.getService().getCurrentUser().id;
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
}
- if (mUser < 0) {
- mUser = UserHandle.USER_SYSTEM;
- }
UserManager userManager = UserManager.get(mProvider.getContext());
if (userManager.getUserInfo(mUser) == null) {
perr.println("Invalid user: " + mUser);
@@ -312,8 +309,8 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result =
- provider.call(resolveCallingPackage(), callListCommand, null, arg);
+ Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ callListCommand, null, arg);
lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
Collections.sort(lines);
} catch (RemoteException e) {
@@ -337,7 +334,8 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle b = provider.call(resolveCallingPackage(), callGetCommand, key, arg);
+ Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ callGetCommand, key, arg);
if (b != null) {
result = b.getPairValue();
}
@@ -374,7 +372,8 @@
if (makeDefault) {
arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
- provider.call(resolveCallingPackage(), callPutCommand, key, arg);
+ provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ callPutCommand, key, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
@@ -397,8 +396,8 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result =
- provider.call(resolveCallingPackage(), callDeleteCommand, key, arg);
+ Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ callDeleteCommand, key, arg);
return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
@@ -424,7 +423,7 @@
}
String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- provider.call(packageName, callResetCommand, null, arg);
+ provider.call(packageName, Settings.AUTHORITY, callResetCommand, null, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index ae2ca3f..521163f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -377,6 +377,13 @@
@GuardedBy("mLock")
public boolean insertSettingLocked(String name, String value, String tag,
boolean makeDefault, String packageName) {
+ return insertSettingLocked(name, value, tag, makeDefault, false, packageName);
+ }
+
+ // The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
+ public boolean insertSettingLocked(String name, String value, String tag,
+ boolean makeDefault, boolean forceNonSystemPackage, String packageName) {
if (TextUtils.isEmpty(name)) {
return false;
}
@@ -387,7 +394,7 @@
Setting newState;
if (oldState != null) {
- if (!oldState.update(value, makeDefault, packageName, tag, false)) {
+ if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) {
return false;
}
newState = oldState;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
new file mode 100644
index 0000000..9d0462e
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import libcore.io.Streams;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Tests for {@link DeviceConfigService}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeviceConfigServiceTest {
+ /**
+ * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+ * API.
+ */
+ private static final Uri CONFIG_CONTENT_URI =
+ Uri.parse("content://" + Settings.AUTHORITY + "/config");
+ private static final String sNamespace = "namespace1";
+ private static final String sKey = "key1";
+ private static final String sValue = "value1";
+
+ private ContentResolver mContentResolver;
+
+ @Before
+ public void setUp() {
+ mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
+ }
+
+ @After
+ public void cleanUp() {
+ deleteFromContentProvider(mContentResolver, sNamespace, sKey);
+ }
+
+ @Test
+ public void testPut() throws Exception {
+ final String newNamespace = "namespace2";
+ final String newValue = "value2";
+
+ String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ assertNull(result);
+
+ try {
+ executeShellCommand("device_config put " + sNamespace + " " + sKey + " " + sValue);
+ executeShellCommand("device_config put " + newNamespace + " " + sKey + " " + newValue);
+
+ result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ assertEquals(sValue, result);
+ result = getFromContentProvider(mContentResolver, newNamespace, sKey);
+ assertEquals(newValue, result);
+ } finally {
+ deleteFromContentProvider(mContentResolver, newNamespace, sKey);
+ }
+ }
+
+ @Test
+ public void testPut_invalidArgs() throws Exception {
+ // missing sNamespace
+ executeShellCommand("device_config put " + sKey + " " + sValue);
+ String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ // still null
+ assertNull(result);
+
+ // too many arguments
+ executeShellCommand(
+ "device_config put " + sNamespace + " " + sKey + " " + sValue + " extra_arg");
+ result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ // still null
+ assertNull(result);
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ final String newNamespace = "namespace2";
+
+ putWithContentProvider(mContentResolver, sNamespace, sKey, sValue);
+ putWithContentProvider(mContentResolver, newNamespace, sKey, sValue);
+ String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ assertEquals(sValue, result);
+ result = getFromContentProvider(mContentResolver, newNamespace, sKey);
+ assertEquals(sValue, result);
+
+ try {
+ executeShellCommand("device_config delete " + sNamespace + " " + sKey);
+ // sKey is deleted from sNamespace
+ result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ assertNull(result);
+ // sKey is not deleted from newNamespace
+ result = getFromContentProvider(mContentResolver, newNamespace, sKey);
+ assertEquals(sValue, result);
+ } finally {
+ deleteFromContentProvider(mContentResolver, newNamespace, sKey);
+ }
+ }
+
+ @Test
+ public void testDelete_invalidArgs() throws Exception {
+ putWithContentProvider(mContentResolver, sNamespace, sKey, sValue);
+ String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ assertEquals(sValue, result);
+
+ // missing sNamespace
+ executeShellCommand("device_config delete " + sKey);
+ result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ // sValue was not deleted
+ assertEquals(sValue, result);
+
+ // too many arguments
+ executeShellCommand("device_config delete " + sNamespace + " " + sKey + " extra_arg");
+ result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ // sValue was not deleted
+ assertEquals(sValue, result);
+ }
+
+ @Test
+ public void testReset() throws Exception {
+ String newValue = "value2";
+
+ // make sValue the default value
+ executeShellCommand(
+ "device_config put " + sNamespace + " " + sKey + " " + sValue + " default");
+ // make newValue the current value
+ executeShellCommand(
+ "device_config put " + sNamespace + " " + sKey + " " + newValue);
+ String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ assertEquals(newValue, result);
+
+ // reset values that were set by untrusted packages
+ executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
+ result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ // the default value has been restored
+ assertEquals(sValue, result);
+
+ // clear values that were set by untrusted packages
+ executeShellCommand("device_config reset trusted_defaults " + sNamespace);
+ result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+ // even the default value is gone now
+ assertNull(result);
+ }
+
+ private static void executeShellCommand(String command) throws IOException {
+ InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().executeShellCommand(command).getFileDescriptor());
+ Streams.readFully(is);
+ }
+
+ private static void putWithContentProvider(ContentResolver resolver, String namespace,
+ String key, String value) {
+ putWithContentProvider(resolver, namespace, key, value, false);
+ }
+
+ private static void putWithContentProvider(ContentResolver resolver, String namespace,
+ String key, String value, boolean makeDefault) {
+ String compositeName = namespace + "/" + key;
+ Bundle args = new Bundle();
+ args.putString(Settings.NameValueTable.VALUE, value);
+ if (makeDefault) {
+ args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
+ }
+ resolver.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
+ }
+
+ private static String getFromContentProvider(ContentResolver resolver, String namespace,
+ String key) {
+ String compositeName = namespace + "/" + key;
+ Bundle result = resolver.call(
+ CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, compositeName, null);
+ assertNotNull(result);
+ return result.getString(Settings.NameValueTable.VALUE);
+ }
+
+ private static boolean deleteFromContentProvider(ContentResolver resolver, String namespace,
+ String key) {
+ String compositeName = namespace + "/" + key;
+ Bundle result = resolver.call(
+ CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
+ assertNotNull(result);
+ return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cb55231..5fe08aa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -51,6 +51,7 @@
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.REMOVE_TASKS" />
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />
<uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -130,6 +131,7 @@
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
+ <uses-permission android:name="android.permission.MANAGE_CONTENT_CAPTURE" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SET_TIME" />
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index dc6a6126..9595e28 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -28,7 +28,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तुमचा बग रीपोर्ट स्क्रीनशॉटशिवाय शेअर करण्यासाठी टॅप करा किंवा स्क्रीनशॉट पूर्ण होण्याची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाईल वाचणे शक्य झाले नाही"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a00baad..75492f3 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -60,6 +60,8 @@
"androidx.lifecycle_lifecycle-extensions",
"SystemUI-tags",
"SystemUI-proto",
+ "dagger2-2.19",
+ "jsr330"
],
manifest: "AndroidManifest.xml",
@@ -73,6 +75,8 @@
"--extra-packages",
"com.android.keyguard",
],
+
+ annotation_processors: ["dagger2-compiler-2.19"],
}
android_library {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0b9b27f..7d53c2f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -223,6 +223,9 @@
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
+ <!-- Permission to change the display color -->
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/legacy/recents/res/values-es/strings.xml b/packages/SystemUI/legacy/recents/res/values-es/strings.xml
index b70f318..4f04a12 100644
--- a/packages/SystemUI/legacy/recents/res/values-es/strings.xml
+++ b/packages/SystemUI/legacy/recents/res/values-es/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aplicaciones recientes."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ignorar la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
+ <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Cerrar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Se ha ignorado la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Se han ignorado todas las aplicaciones recientes."</string>
<string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre la información de la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
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 7cb63ea..1a18f60 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -18,6 +18,8 @@
import com.android.systemui.plugins.annotations.ProvidesInterface;
+import java.util.TimeZone;
+
/**
* This plugin is used to replace main clock in keyguard.
*/
@@ -55,4 +57,9 @@
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
*/
default void setDarkAmount(float darkAmount) {}
+
+ /**
+ * Notifies that the time zone has changed.
+ */
+ default void onTimeZoneChanged(TimeZone timeZone) {}
}
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 555f443..9245c30 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -31,7 +31,7 @@
<!-- Max Height of the sliding KeyguardSecurityContainer
(includes 2x keyguard_security_view_top_margin) -->
- <dimen name="keyguard_security_max_height">450dp</dimen>
+ <dimen name="keyguard_security_max_height">455dp</dimen>
<!-- Margin around the various security views -->
<dimen name="keyguard_security_view_top_margin">8dp</dimen>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml
deleted file mode 100644
index 3cc98d8..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="500"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M 0,0 c 31.33333,0 156.66667,0 188,0 "
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml
deleted file mode 100644
index eda843d..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="33"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -12,18 L -12,18" />
- <objectAnimator
- android:duration="500"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -12,18 c 31.33333,0 156.66667,0 188,0 "
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml
deleted file mode 100644
index cab3d5c..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="67"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -24,36 L -24,36" />
- <objectAnimator
- android:duration="500"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -24,36 c 31.33333,0 156.66667,0 188,0 "
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml
deleted file mode 100644
index e435d9a..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="150"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="100"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml
deleted file mode 100644
index e31a7db..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="183"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="100"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml
deleted file mode 100644
index 2409612..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="217"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="100"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/fab_elevation.xml b/packages/SystemUI/res/anim/fab_elevation.xml
deleted file mode 100644
index 2c76a86..0000000
--- a/packages/SystemUI/res/anim/fab_elevation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:state_pressed="true">
- <set>
- <objectAnimator
- android:duration="@android:integer/config_shortAnimTime"
- android:propertyName="translationZ"
- android:valueTo="@dimen/fab_press_translation_z"
- android:valueType="floatType" />
- </set>
- </item>
- <item>
- <set>
- <objectAnimator
- android:duration="@android:integer/config_shortAnimTime"
- android:propertyName="translationZ"
- android:valueTo="0"
- android:valueType="floatType" />
- </set>
- </item>
-</selector>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml
deleted file mode 100644
index 8fdad80..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="-90.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml
deleted file mode 100644
index 3c3c131..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml
deleted file mode 100644
index 57132e1..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_landscape_to_rotate_animation_interpolator_0" />
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_landscape_to_rotate_animation_interpolator_0" />
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="400"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="45.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
deleted file mode 100644
index ad2a5fa..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
deleted file mode 100644
index cdb7890..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="-221.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
deleted file mode 100644
index 46100b4..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="400"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
deleted file mode 100644
index 8f6d24d..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="400"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="-135.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
deleted file mode 100644
index 300ed53..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="66"
- android:propertyName="pathData"
- android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueTo="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="216"
- android:propertyName="pathData"
- android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml
deleted file mode 100644
index ad2a5fa..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml
deleted file mode 100644
index c152152..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="-180.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml
deleted file mode 100644
index b2c1eb8..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="200"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml
deleted file mode 100644
index 2a9bbe3..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleX"
- android:valueFrom="0.909"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="166"
- android:propertyName="scaleX"
- android:valueFrom="0.909"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_landscape_animation_interpolator_0" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleY"
- android:valueFrom="0.909"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="166"
- android:propertyName="scaleY"
- android:valueFrom="0.909"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_landscape_animation_interpolator_0" />
- </set>
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="45.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
deleted file mode 100644
index ce26770..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleX"
- android:valueFrom="0.9"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleY"
- android:valueFrom="0.9"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
deleted file mode 100644
index 6e8941d..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="-221.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
deleted file mode 100644
index 3c3c131..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
deleted file mode 100644
index fd8e4f8..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="rotation"
- android:valueFrom="-135.0"
- android:valueTo="-135.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="400"
- android:propertyName="rotation"
- android:valueFrom="-135.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
deleted file mode 100644
index a77a536..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="pathData"
- android:valueFrom="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="500"
- android:propertyName="pathData"
- android:valueFrom="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueTo="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_1.xml b/packages/SystemUI/res/anim/ic_signal_blink_1.xml
deleted file mode 100644
index 64580d1..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_1.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/linear_interpolator"
- android:duration="@integer/carrier_network_change_anim_time"
- android:repeatCount="-1">
-
- <propertyValuesHolder
- android:propertyName="fillColor"
- android:valueType="colorType">
- <keyframe
- android:fraction="0.0"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.32"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.33"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="1.0"
- android:value="?attr/backgroundColor"/>
- </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_2.xml b/packages/SystemUI/res/anim/ic_signal_blink_2.xml
deleted file mode 100644
index f055cd07..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_2.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/linear_interpolator"
- android:duration="@integer/carrier_network_change_anim_time"
- android:repeatCount="-1">
-
- <propertyValuesHolder
- android:propertyName="fillColor"
- android:valueType="colorType">
- <keyframe
- android:fraction="0.0"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.32"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.33"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.66"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.67"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="1.0"
- android:value="?attr/backgroundColor"/>
- </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_3.xml b/packages/SystemUI/res/anim/ic_signal_blink_3.xml
deleted file mode 100644
index abcd774..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_3.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/linear_interpolator"
- android:duration="@integer/carrier_network_change_anim_time"
- android:repeatCount="-1">
-
- <propertyValuesHolder
- android:propertyName="fillColor"
- android:valueType="colorType">
- <keyframe
- android:fraction="0.0"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.66"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.67"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="1.0"
- android:value="?attr/fillColor"/>
- </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/system_out.xml b/packages/SystemUI/res/anim/system_out.xml
deleted file mode 100644
index 4717e47..0000000
--- a/packages/SystemUI/res/anim/system_out.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- >
- <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
- android:duration="@android:integer/config_longAnimTime"
- />
-</set>
diff --git a/packages/SystemUI/res/drawable/car_ic_music.xml b/packages/SystemUI/res/drawable/car_ic_music.xml
deleted file mode 100644
index f90cd69..0000000
--- a/packages/SystemUI/res/drawable/car_ic_music.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:fillAlpha=".1"
- android:strokeAlpha=".1"
- android:pathData="M0 0h48v48H0z" />
- <path
- android:fillColor="@color/car_grey_50"
- android:pathData="M24 2C14.06 2 6 10.06 6 20v14c0 3.31 2.69 6 6 6h6V24h-8v-4c0-7.73 6.27-14
-14-14s14 6.27 14 14v4h-8v16h6c3.31 0 6-2.69 6-6V20c0-9.94-8.06-18-18-18z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_ic_navigation.xml b/packages/SystemUI/res/drawable/car_ic_navigation.xml
deleted file mode 100644
index 328efa0..0000000
--- a/packages/SystemUI/res/drawable/car_ic_navigation.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="37dp"
- android:viewportWidth="32.0"
- android:viewportHeight="37.0">
- <path
- android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
- android:strokeColor="#00000000"
- android:fillType="evenOdd"
- android:fillColor="@color/car_grey_50"
- android:strokeWidth="1"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml
deleted file mode 100644
index 61d937b90..0000000
--- a/packages/SystemUI/res/drawable/car_ic_notification.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
-4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
-1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
-9v12z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml
deleted file mode 100644
index 4651dcb..0000000
--- a/packages/SystemUI/res/drawable/car_ic_overview.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:pathData="M0 0h48v48H0z" />
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
-0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
deleted file mode 100644
index 25b449a..0000000
--- a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?android:attr/colorBackgroundFloating" />
- <corners
- android:bottomLeftRadius="@dimen/car_radius_3"
- android:topLeftRadius="0dp"
- android:bottomRightRadius="@dimen/car_radius_3"
- android:topRightRadius="0dp"
- />
-</shape>
diff --git a/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml b/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml
deleted file mode 100644
index 9e71cbe..0000000
--- a/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/dismiss_all_shape" >
- <target
- android:name="3"
- android:animation="@anim/dismiss_all_shape_animation_3" />
- <target
- android:name="rectangle_path_1_2"
- android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1_2" />
- <target
- android:name="2"
- android:animation="@anim/dismiss_all_shape_animation_2" />
- <target
- android:name="rectangle_path_1_1"
- android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1_1" />
- <target
- android:name="1"
- android:animation="@anim/dismiss_all_shape_animation_1" />
- <target
- android:name="rectangle_path_1"
- android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1" />
-</animated-vector>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
new file mode 100644
index 0000000..b2307e7
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.systemui.bubbles.BubbleExpandedViewContainer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/bubble_expanded_view">
+
+ <!-- TODO: header -->
+
+ <View
+ android:id="@+id/pointer_view"
+ android:layout_width="@dimen/bubble_pointer_width"
+ android:layout_height="@dimen/bubble_pointer_height"
+ />
+</com.android.systemui.bubbles.BubbleExpandedViewContainer>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
deleted file mode 100644
index d568d0d..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@drawable/system_bar_background">
-
- <LinearLayout
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:orientation="horizontal"
- android:id="@+id/nav_buttons"
- android:gravity="left"
- android:paddingLeft="30dp"
- android:layout_weight="1"
- android:animateLayoutChanges="true">
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/home"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- android:src="@drawable/car_ic_overview"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/hvac"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
- systemui:broadcast="true"
- android:src="@drawable/car_ic_hvac"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="right"
- android:orientation="horizontal">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:src="@drawable/car_ic_notification"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:alpha="0.7"
- />
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_vertical"
- android:paddingRight="20dp"
- />
-
- <Space
- android:layout_width="10dp"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
deleted file mode 100644
index 4ba6c06..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@drawable/system_bar_background">
-
- <LinearLayout
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:orientation="horizontal"
- android:id="@+id/nav_buttons"
- android:gravity="left"
- android:paddingLeft="30dp"
- android:layout_weight="1"
- android:animateLayoutChanges="true">
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/home"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- android:src="@drawable/car_ic_overview"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/hvac"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
- systemui:broadcast="true"
- android:src="@drawable/car_ic_hvac"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
- </LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
deleted file mode 100644
index 91ba026..0000000
--- a/packages/SystemUI/res/layout/car_right_navigation_bar.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:background="@drawable/system_bar_background">
-
- <LinearLayout
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:id="@+id/nav_buttons"
- android:orientation="vertical"
- android:gravity="top"
- android:paddingTop="30dp"
- android:layout_weight="1"
- android:background="@drawable/system_bar_background"
- android:animateLayoutChanges="true">
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/home"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- android:src="@drawable/car_ic_overview"
- android:background="?android:attr/selectableItemBackground"
- android:paddingTop="30dp"
- android:paddingBottom="30dp"
- />
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/hvac"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
- systemui:broadcast="true"
- android:src="@drawable/car_ic_hvac"
- android:background="?android:attr/selectableItemBackground"
- android:paddingTop="30dp"
- android:paddingBottom="30dp"
- />
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="bottom"
- android:orientation="vertical">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:src="@drawable/car_ic_notification"
- android:background="?android:attr/selectableItemBackground"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:alpha="0.7"
- />
-
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_horizontal"
- android:paddingBottom="20dp"
- />
-
- <Space
- android:layout_height="10dp"
- android:layout_width="match_parent"/>
-
- </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml
deleted file mode 100644
index f2ef301..0000000
--- a/packages/SystemUI/res/layout/car_status_bar_header.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- Extends LinearLayout -->
-<com.android.systemui.qs.car.CarStatusBarHeader
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_qs_header_system_icons_area_height"
- android:paddingStart="8dp"
- android:paddingEnd="8dp" >
-
- <include layout="@layout/system_icons"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical|end"
- android:layout_weight="1"
- />
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_vertical|end"
- />
-</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
deleted file mode 100644
index e16014b..0000000
--- a/packages/SystemUI/res/layout/car_top_navigation_bar.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@drawable/system_bar_background">
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_vertical"
- />
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
deleted file mode 100644
index a6beaa1..0000000
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<androidx.car.widget.PagedListView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:background="@drawable/car_card_rounded_background"
- android:id="@+id/volume_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/car_margin"
- android:layout_marginEnd="@dimen/car_margin"
- android:minWidth="@dimen/volume_dialog_panel_width"
- android:theme="@style/Theme.Car.NoActionBar"
- app:dividerStartMargin="@dimen/car_keyline_1"
- app:dividerEndMargin="@dimen/car_keyline_1"
- app:gutter="none"
- app:showPagedListViewDivider="true"
- app:scrollBarEnabled="false" />
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 0cc3c9e..34c208a 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -56,11 +56,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
- android:layout="@layout/car_fullscreen_user_switcher"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index de1ef3d..6b25387 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Hulpboodskapgebied"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bevestig"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Raak die vingerafdruksensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Vingerafdrukikoon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Soek tans vir jou …"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Oudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Kopstuk"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Invoer"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Skakel tans aan …"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Helderheid"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Outo-draai"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Kennisgewings"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Jy sal nie meer hierdie kennisgewings sien nie"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Hierdie kennisgewings sal geminimeer word"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Hierdie kennisgewings sal stil gewys word"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Hierdie kennisgewings sal vir jou \'n klank speel"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Jy maak hierdie kennisgewings gewoonlik toe. \nMoet ons aanhou om hulle te wys?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Hou aan om hierdie kennisgewings te wys?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop kennisgewings"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Hou aan wys"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimeer"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Wys sonder klank"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Hou aan om kennisgewings van hierdie program af te wys?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Hierdie kennisgewings kan nie afgeskakel word nie"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Hierdie program gebruik tans die kamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Hierdie program gebruik tans die mikrofoon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Hierdie program wys tans bo-oor ander programme op jou skerm."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Instellings"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Het dit"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Stort SysUI-hoop"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> gebruik tans jou <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Programme gebruik tans jou <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Open app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Kanselleer"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Goed"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Stellings"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> gebruik die afgelope <xliff:g id="TIME">%3$d</xliff:g> min. jou <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> gebruik tans jou <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> gebruik tans jou <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Bekyk details"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Program wat jou <xliff:g id="TYPES_LIST">%s</xliff:g> gebruik"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Programme wat jou <xliff:g id="TYPES_LIST">%s</xliff:g> gebruik"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" en "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ligging"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofoon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ander programme</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ander program</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 04f5d4c..db97510 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"የእገዛ መልዕክት አካባቢ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"አረጋግጥ"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"የጣት አሻራ ዳሳሹን ይንኩ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"የጣት አሻራ አዶ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"እርስዎን በመፈለግ ላይ…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ኦዲዮ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ማዳመጫ"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ግቤት"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"በማብራት ላይ..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ብሩህነት"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"በራስ ሰር አሽከርክር"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"ማሳወቂያዎች"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"እነዚህን ማሳወቂያዎችን ከእንግዲህ አይመለከቷቸውም"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"እነዚህ ማሳወቂያዎች እንዲያንሱ ይደረጋሉ"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"እነዚህ ማሳወቂያዎች በፀጥታ ይታያሉ"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"እነዚህ ማሳወቂያዎች እርስዎን ያነቃሉ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"አብዛኛውን ጊዜ እነዚህን ማሳወቂያዎች ያሰናብቷቸዋል። \nመታየታቸው ይቀጥል??"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"እነዚህን ማሳወቂያዎች ማሳየት ይቀጥሉ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ማሳወቂያዎችን አስቁም"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"ማሳየትን ቀጥል"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"አሳንስ"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"በፀጥታ አሳይ"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ከዚህ መተግበሪያ ማሳወቂያዎችን ማሳየት ይቀጥል?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"እነዚህ ማሳወቂያዎች ሊጠፉ አይችሉም"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"በ<xliff:g id="APP_NAME">%1$s</xliff:g> በኩል"</string>
<string name="appops_camera" msgid="8100147441602585776">"ይህ መተግበሪያ ካሜራውን እየተጠቀመ ነው።"</string>
<string name="appops_microphone" msgid="741508267659494555">"ይህ መተግበሪያ ማይክሮፎኑን እየተጠቀመ ነው።"</string>
<string name="appops_overlay" msgid="6165912637560323464">"ይህ መተግበሪያ በማያ ገጽዎ ላይ ባሉ ሌሎች መተግበሪያዎች ላይ እያሳየ ነው።"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ቅንብሮች"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ገባኝ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI Heap አራግፍ"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> የእርስዎን <xliff:g id="TYPES_LIST">%2$s</xliff:g> እየተጠቀመ ነው።"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"መተግበሪያዎች የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> እየተጠቀሙ ነው።"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"መተግበሪያን ክፈት"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ይቅር"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"እሺ"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ቅንብሮች"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> የእርስዎን <xliff:g id="TYPE">%2$s</xliff:g> ላለፉት <xliff:g id="TIME">%3$d</xliff:g> ደቂቃዎች እየተጠቀመ ነው"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> የእርስዎን <xliff:g id="TYPE">%2$s</xliff:g> እየተጠቀመ ነው"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> የእርስዎን <xliff:g id="TYPES_LIST">%2$s</xliff:g> እየተጠቀመ ነው"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"ዝርዝሮችን አሳይ"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> የሚጠቀሙ መተግበሪያዎች"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> የሚጠቀሙ መተግበሪያዎች"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"፣ "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" እና "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ካሜራ"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"አካባቢ"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"ማይክሮፎን"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ሌሎች መተግበሪያዎች</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ሌሎች መተግበሪያዎች</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 11144ee..ff98366 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"منطقة رسالة المساعدة"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأكيد"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"المس جهاز استشعار بصمات الإصبع"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"رمز بصمة الإصبع"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"جارٍ البحث عن وجهك…"</string>
@@ -294,6 +296,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"سماعة الرأس"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"الإدخال"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"جارٍ التفعيل…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"السطوع"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"دوران تلقائي"</string>
@@ -615,12 +619,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"أنت تتجاهل عادةً هذه الإشعارات. \nهل تريد الاستمرار في عرضها؟"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"هل تريد الاستمرار في تلقي هذه الإشعارات؟"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"إيقاف الإشعارات"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"الاستمرار في تلقّي الإشعارات"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"تصغير"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"عرض بدون تنبيه صوتي"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"العرض والتنبيه"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"هل تريد الاستمرار في تلقي إشعارات من هذا التطبيق؟"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"يتعذَّر إيقاف هذه الإشعارات."</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"عبر <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"يستخدم هذا التطبيق الكاميرا."</string>
<string name="appops_microphone" msgid="741508267659494555">"يستخدم هذا التطبيق الميكروفون."</string>
<string name="appops_overlay" msgid="6165912637560323464">"يتم عرض هذا التطبيق فوق التطبيقات الأخرى على شاشتك."</string>
@@ -875,16 +887,25 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"الإعدادات"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"حسنًا"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"تفريغ ذاكرة SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"التطبيق <xliff:g id="APP">%1$s</xliff:g> يستخدم <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"تستخدم التطبيقات <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"فتح التطبيق"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"إلغاء"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"موافق"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"الإعدادات"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"التطبيق <xliff:g id="APP">%1$s</xliff:g> يستخدم <xliff:g id="TYPE">%2$s</xliff:g> منذ <xliff:g id="TIME">%3$d</xliff:g> من الدقائق مضت."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"التطبيقات <xliff:g id="APPS">%1$s</xliff:g> تستخدم <xliff:g id="TYPE">%2$s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"التطبيق <xliff:g id="APP">%1$s</xliff:g> يستخدم <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"عرض التفاصيل"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"التطبيق الذي يستخدِم <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"التطبيقات التي تستخدِم <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"، "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" و "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"الكاميرا"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"الموقع"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"الميكروفون"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="zero"><xliff:g id="NUM_APPS_1">%d</xliff:g> تطبيق آخر</item>
+ <item quantity="two">تطبيقان (<xliff:g id="NUM_APPS_1">%d</xliff:g>) آخران</item>
+ <item quantity="few"><xliff:g id="NUM_APPS_1">%d</xliff:g> تطبيقات أخرى</item>
+ <item quantity="many"><xliff:g id="NUM_APPS_1">%d</xliff:g> تطبيقًا آخر</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> تطبيق آخر</item>
+ <item quantity="one">تطبيق واحد (<xliff:g id="NUM_APPS_0">%d</xliff:g>) آخر</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index c60d2d4..d7b218d 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"বাতিল কৰক"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"সহায় বাৰ্তাৰ ক্ষেত্ৰ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"নিশ্চিত কৰক"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপোনাৰ মুখমণ্ডল বিচাৰি আছে…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"অডিঅ’"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"হেডছেট"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ইনপুট"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"অন কৰি থকা হৈছে…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"উজ্জ্বলতা"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"স্বয়ং-ঘূৰ্ণন"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"জাননীসমূহ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"আপোনাক এই জাননীসমূহ আৰু দেখুওৱা নহ\'ব"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"এই জাননীসমূহ মিনিমাইজ কৰি থোৱা হ\'ব"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"এই জাননী নিৰৱে দেখুওৱা হ’ব"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"এই জাননীবোৰে আপোনাক সতৰ্ক কৰিব"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"আপুনি সাধাৰণতে এই জাননীসমূহ অগ্ৰাহ্য কৰে। \nসেইবোৰ দেখুওৱাই থাকিব লাগিবনে?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"এই জাননীসমূহ দেখুওৱাই থাকিব লাগিবনে?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"জাননী বন্ধ কৰক"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"দেখুওৱাই থাকক"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"সৰু কৰক"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"নিৰৱে দেখুৱাওক"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"এই এপটোৰ জাননী দেখুওৱাই থাকিব লাগিবনে?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"এই জাননীসমূহ বন্ধ কৰিব নোৱাৰি"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জৰিয়তে"</string>
<string name="appops_camera" msgid="8100147441602585776">"এই এপে কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
<string name="appops_microphone" msgid="741508267659494555">"এই এপে মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string>
<string name="appops_overlay" msgid="6165912637560323464">"এই এপটো আপোনাৰ স্ক্ৰীণত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ আছে।"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ছেটিংবোৰ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"বুজি পালোঁ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI হীপ ডাম্প কৰক"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে।"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"এপ্লিকেশ্বনসমূহে আপোনাৰ <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যৱহাৰ কৰি আছে।"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"এপ্ খোলক"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"বাতিল কৰক"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ঠিক আছে"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ছেটিংসমূহ"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g>এ যোৱা <xliff:g id="TIME">%3$d</xliff:g> মিনিটৰ পৰা আপোনাৰ <xliff:g id="TYPE">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="TYPE">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"সবিশেষ চাওক"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"এপটোৱে আপোনাৰ <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যৱহাৰ কৰি আছে"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"এপসমূহে আপোনাৰ <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যৱহাৰ কৰি আছে"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" আৰু "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"কেমেৰা"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"অৱস্থান"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"মাইক্ৰ\'ফ\'ন"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g>টা অন্য এপ্</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>টা অন্য এপ্</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index b47d5ff..9c2cbb9 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Ləğv et"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yardım mesajı bölməsi"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Təsdiq"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Barmaq izi sensoruna klikləyin"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmaq izi ikonası"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Siz axtarılırsınız…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Qulaqlıq"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Giriş"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Aktiv edilir..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Parlaqlıq"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Avtodönüş"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Adətən bu bildirişləri rədd edirsiniz. \nBildirişlər göstərilsin?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Bu bildirişlər göstərilməyə davam edilsin?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bildirişləri dayandırın"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Göstərməyə davam edin"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Kiçildin"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Səssiz göstərin"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Göstərin və xəbərdar edin"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu tətbiqin bildirişləri göstərilməyə davam edilsin?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirişlər deaktiv edilə bilməz"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> vasitəsilə"</string>
<string name="appops_camera" msgid="8100147441602585776">"Bu tətbiq kameradan istifadə edir."</string>
<string name="appops_microphone" msgid="741508267659494555">"Bu tətbiq mikrofondan istifadə edir."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Bu tətbiqdə ekranda digər tətbiqlərin üzərində göstərilir."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Ayarlar"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Anladım"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="TYPES_LIST">%2$s</xliff:g> tətbiqlərindən istifadə edir."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Tətbiqlər <xliff:g id="TYPES_LIST">%s</xliff:g> istifadə edir."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Tətbiqi açın"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Ləğv edin"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Oldu"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Ayarlar"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> son <xliff:g id="TIME">%3$d</xliff:g> dəqiqədir ki, <xliff:g id="TYPE">%2$s</xliff:g> tətbiqindən istifadə edir"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> tətbiqindən istifadə edir"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="TYPES_LIST">%2$s</xliff:g> tətbiqlərindən istifadə edir"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Detallara baxın"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> tətbiqindən istifadə edən tətbiq"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g> tətbiqindən istifadə edən tətbiqlər"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" və "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"məkan"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Digər <xliff:g id="NUM_APPS_1">%d</xliff:g> tətbiq</item>
+ <item quantity="one">Digər <xliff:g id="NUM_APPS_0">%d</xliff:g> tətbiq</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 8538565..13919bb 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Oblast poruke za pomoć"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdi"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor za otisak prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otiska prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
@@ -291,6 +293,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Slušalice"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Unos"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Uključuje se..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Osvetljenost"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatska rotacija"</string>
@@ -601,21 +605,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Obaveštenja"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Više nećete videti ova obaveštenja"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ova obaveštenja će se umanjiti"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ova obaveštenja će se prikazivati bez zvuka"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ova obaveštenja će vas upozoravati"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Obično odbacujete ova obaveštenja. \nŽelite li da se i dalje prikazuju?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Želite li da se ova obaveštenja i dalje prikazuju?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Prestani da prikazuješ obaveštenja"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Nastavi da prikazuješ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Umanji"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Prikaži bez zvuka"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obaveštenja iz ove aplikacije i dalje prikazuju?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ne možete da isključite ova obaveštenja"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"preko aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ova aplikacija koristi kameru."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ova aplikacija koristi mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ova aplikacija se prikazuje preko drugih aplikacija na ekranu."</string>
@@ -864,16 +872,22 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Podešavanja"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Važi"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Izdvoji SysUI mem."</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> koristi <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Otvori"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Otkaži"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Potvrdi"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Podešavanja"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> tokom nekoliko poslednjih minuta (<xliff:g id="TIME">%3$d</xliff:g>) koristi <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> koriste <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> koristi <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Prikaži detalje"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacija koja koristi dozvole <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacije koje koriste dozvole <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" i "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kameru"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokaciju"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">I još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacija</item>
+ <item quantity="few">I još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacije</item>
+ <item quantity="other">I još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacija</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index e83b9be..2f2f560 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Скасаваць"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Поле даведачнага паведамлення"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Пацвердзіць"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Дакраніцеся да сканера адбіткаў пальцаў"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок адбіткаў пальцаў"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ідзе пошук вашага твару…"</string>
@@ -294,6 +296,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Гук"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Гарнітура"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Увод"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Уключэнне…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Яркасць"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Аўтапаварот"</string>
@@ -606,21 +610,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Апавяшчэнні"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Вы больш не будзеце бачыць гэтыя апавяшчэнні"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Апавяшчэнні будуць згорнуты"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Гэтыя апавяшчэнні будуць паказвацца без гуку"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Гэтыя апавяшчэнні будуць паказвацца з гукам"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Звычайна вы адхіляеце гэтыя апавяшчэнні. \nПаказваць іх?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Працягваць паказваць гэтыя апавяшчэнні?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Спыніць апавяшчэнні"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Працягваць паказваць"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Згарнуць"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Паказваць бязгучна"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Працягваць паказваць апавяшчэнні гэтай праграмы?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Немагчыма адключыць гэтыя апавяшчэнні"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"праз праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="appops_camera" msgid="8100147441602585776">"Гэта праграма выкарыстоўвае камеру."</string>
<string name="appops_microphone" msgid="741508267659494555">"Гэта праграма выкарыстоўвае мікрафон."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Гэта праграма паказваецца на экране паверх іншых праграм."</string>
@@ -871,16 +879,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Налады"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Зразумела"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Дамп кучы SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Праграма \"<xliff:g id="APP">%1$s</xliff:g>\" выкарыстоўвае: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Праграмы выкарыстоўваюць: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Адкрыць"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Скасаваць"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ОК"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Налады"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Праграма \"<xliff:g id="APP">%1$s</xliff:g>\" на працягу апошніх <xliff:g id="TIME">%3$d</xliff:g> хв. выкарыстоўвае: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Праграма \"<xliff:g id="APPS">%1$s</xliff:g>\" выкарыстоўвае: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Праграма \"<xliff:g id="APP">%1$s</xliff:g>\" выкарыстоўвае: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Падрабязнасці"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Праграма, якая выкарыстоўвае: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Праграмы, якія выкарыстоўваюць: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" і "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камера"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"геалакацыя"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"мікрафон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">яшчэ <xliff:g id="NUM_APPS_1">%d</xliff:g> праграма</item>
+ <item quantity="few">яшчэ <xliff:g id="NUM_APPS_1">%d</xliff:g> праграмы</item>
+ <item quantity="many">яшчэ <xliff:g id="NUM_APPS_1">%d</xliff:g> праграм</item>
+ <item quantity="other">яшчэ <xliff:g id="NUM_APPS_1">%d</xliff:g> праграмы</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index bdc561a..d985976 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област за помощно съобщение"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потвърждаване"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Докоснете сензора за отпечатъци"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатък"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Търсим ви…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Слушалки"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Вход"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Включва се..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Яркост"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматична ориентация"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Известия"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Вече няма да виждате тези известия"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Тези известия ще бъдат намалени"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Тези известия ще се показват без звук"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ще получавате сигнал за тези известия"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Обикновено отхвърляте тези известия. \nИскате ли да продължат да се показват?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Тези известия да продължат ли да се показват?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Спиране на известията"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Да продължат да се показват"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Намаляване"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Показване без звук"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Да продължат ли да се показват известията от това приложение?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Тези известия не могат да бъдат изключени"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"чрез <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Това приложение използва камерата."</string>
<string name="appops_microphone" msgid="741508267659494555">"Това приложение използва микрофона."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Това приложение се показва върху други приложения на екрана."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Настройки"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Разбрах"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> използва <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Някои приложения използват <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Отваряне"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Отказ"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Настройки"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> използва <xliff:g id="TYPE">%2$s</xliff:g> през последните <xliff:g id="TIME">%3$d</xliff:g> минути"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> използват <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> използва <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Преглед на подробностите"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Приложение, което използва <xliff:g id="TYPES_LIST">%s</xliff:g> ви"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Приложения, които използват <xliff:g id="TYPES_LIST">%s</xliff:g> ви"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" и "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камерата"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"местополож."</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"микрофона"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> други приложения</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> друго приложение</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index b22714d..504b3fa 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"সহায়তার মেসেজ দেখানোর জায়গা"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"কনফার্ম করুন"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"আঙ্গুলের ছাপের সেন্সর স্পর্শ করুন"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"আঙ্গুলের ছাপের আইকন"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপনার জন্য খোঁজা হচ্ছে…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"অডিও"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"হেডসেট"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ইনপুট"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"চালু করা হচ্ছে…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"উজ্জ্বলতা"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"স্বতঃ ঘূর্ণায়মান"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"বিজ্ঞপ্তি"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"এই বিজ্ঞপ্তিগুলি আপনাকে আর দেখানো হবে না"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"এই বিজ্ঞপ্তিগুলি ছোট করে দেওয়া হবে"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"এই বিজ্ঞপ্তিগুলি নিঃশব্দে দেখানো হবে"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"এই বিজ্ঞপ্তিগুলি আপনাকে সতর্ক করে দেবে"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"এই বিজ্ঞপ্তিগুলিকে আপনি সাধারণত বাতিল করেন। \nসেগুলি দেখতে চান?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"এই বিজ্ঞপ্তিগুলি পরেও দেখে যেতে চান?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"বিজ্ঞপ্তি বন্ধ করুন"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"দেখতে থাকুন"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ছোট করে দিন"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"নিঃশব্দে দেখুন"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"এই অ্যাপের বিজ্ঞপ্তি পরেও দেখে যেতে চান?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"এই বিজ্ঞপ্তিগুলি বন্ধ করা যাবে না"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর মাধ্যমে"</string>
<string name="appops_camera" msgid="8100147441602585776">"এই অ্যাপটি ক্যামেরা ব্যবহার করছে।"</string>
<string name="appops_microphone" msgid="741508267659494555">"এই অ্যাপটি মাইক্রোফোন ব্যবহার করছে।"</string>
<string name="appops_overlay" msgid="6165912637560323464">"এই অ্যাপটি স্ক্রিনে অন্যান্য অ্যাপের উপরে দেখানো হচ্ছে।"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"সেটিংস"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"বুঝেছি"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> আপনার <xliff:g id="TYPES_LIST">%2$s</xliff:g> ব্যবহার করছে।"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"অ্যাপ্লিকেশনগুলি আপনার <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যবহার করছে।"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"অ্যাপ খুলুন"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"বাতিল করুন"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ঠিক আছে"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"সেটিংস"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> আপনার <xliff:g id="TYPE">%2$s</xliff:g> গত <xliff:g id="TIME">%3$d</xliff:g> মিনিট ধরে ব্যবহার করছে"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> আপনার <xliff:g id="TYPE">%2$s</xliff:g> ব্যবহার করছে"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> আপনার <xliff:g id="TYPES_LIST">%2$s</xliff:g> ব্যবহার করছে"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"বিবরণ দেখুন"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"অ্যাপ আপনার <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যবহার করছে"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"অ্যাপ আপনার <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যবহার করছে"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" এবং "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ক্যামেরা"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"লোকেশন"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"মাইক্রোফোন"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g>টি অন্য অ্যাপ</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>টি অন্য অ্যাপ</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index bd48839..cdb0c65 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Prostor za poruku za pomoć"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdite"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor za otisak prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona za otisak prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
@@ -291,6 +293,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Slušalice"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Ulaz"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Uključivanje…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Osvjetljenje"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatsko rotiranje"</string>
@@ -603,17 +607,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Obavještenja"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nećete više vidjeti ova obavještenja"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ova obavještenja će se minimizirati"</string>
- <string name="notification_channel_silenced" msgid="2877199534497961942">"Ove obavijesti prikazivat će se tiho"</string>
- <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ove obavijesti imat će zvučni signal"</string>
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ova upozorenja bit će prikazana bez zvuka"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ova obavještenja će vas upozoriti"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Obično odbacujete ova obavještenja. \nNastaviti ih prikazivati?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Nastaviti prikazivanje ovih obavještenja?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Zaustavi obavještenja"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Nastavi prikazivanje"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimiziraj"</string>
- <string name="inline_silent_button_silent" msgid="4411510650503783646">"Prikaži tiho"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Prikaži uz zvučni signal"</string>
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Prikaži bez zvuka"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Nastaviti prikazivanje obavještenja iz ove aplikacije?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ova obavještenja nije moguće isključiti"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"preko aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ova aplikacija koristi kameru."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ova aplikacija koristi mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ova aplikacija prekriva druge aplikacije na ekranu."</string>
@@ -862,16 +874,22 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Postavke"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Razumijem"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Izdvoji SysUI mem."</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> koristi <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Otvori"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Otkaži"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Uredu"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Postavke"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> koristi <xliff:g id="TYPE">%2$s</xliff:g> u vremenskom periodu od <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Aplikacije <xliff:g id="APPS">%1$s</xliff:g> koriste <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> koristi <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Prikaži detalje"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacija koristi odobrenja <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacije koriste odobrenja <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" i "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kameru"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokaciju"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> druga aplikacija</item>
+ <item quantity="few"><xliff:g id="NUM_APPS_1">%d</xliff:g> druge aplikacije</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> drugih aplikacija</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 0e91142..16770dc 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Àrea de missatge d\'ajuda"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirma"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor d\'empremtes digitals"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona d\'empremta digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"S\'està cercant la teva cara…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Àudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Auriculars"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrada"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"S\'està activant…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brillantor"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Gira automàticament"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificacions"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Ja no veuràs aquestes notificacions"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Aquestes notificacions es minimitzaran"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Aquestes notificacions es mostraran de manera silenciosa"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Aquestes notificacions t\'enviaran una alerta"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Normalment ignores aquestes notificacions. \nVols que es continuïn mostrant?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Vols continuar rebent aquestes notificacions?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Deixa d\'enviar notificacions"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continua rebent"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimitza"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostra de manera silenciosa"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vols continuar rebent notificacions d\'aquesta aplicació?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Aquestes notificacions no es poden desactivar"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"mitjançant <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Aquesta aplicació utilitza la càmera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Aquesta aplicació utilitza el micròfon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Aquesta aplicació es mostra sobre altres aplicacions a la pantalla."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Configuració"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"D\'acord"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Aboca espai de SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> està fent servir el següent: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Algunes aplicacions estan fent servir el següent: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Obre l\'app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancel·la"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"D\'acord"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Configuració"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Durant els <xliff:g id="TIME">%3$d</xliff:g> últims minuts, <xliff:g id="APP">%1$s</xliff:g> ha estat fent servir el següent: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> estan fent servir el següent: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> està fent servir el següent: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Mostra els detalls"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicació que fa servir: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicacions que fan servir: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" i "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"càmera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ubicació"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"micròfon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplicacions més</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplicació més</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index b246579..7d1976b 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Oblast pro zprávu nápovědy"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdit"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotkněte se snímače otisků prstů"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otisku prstu"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hledáme vás…"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Sluchátka"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Vstup"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Zapínání…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Jas"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatické otáčení"</string>
@@ -604,21 +608,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Oznámení"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Tato oznámení již nebudete dostávat"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Tato oznámení budou minimalizována"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Tato oznámení se budou zobrazovat tiše"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Tato oznámení vás upozorní"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Tato oznámení obvykle odmítáte. \nChcete je nadále zobrazovat?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Mají se tato oznámení nadále zobrazovat?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Přestat zobrazovat oznámení"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Nadále zobrazovat"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimalizovat"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Zobrazovat tiše"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Mají se oznámení z této aplikace nadále zobrazovat?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Tato oznámení nelze deaktivovat"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"prostřednictvím aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Tato aplikace využívá fotoaparát."</string>
<string name="appops_microphone" msgid="741508267659494555">"Tato aplikace využívá mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Tato aplikace se zobrazuje přes ostatní aplikace na obrazovce."</string>
@@ -869,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Nastavení"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Rozumím"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Výpis haldy SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Aplikace <xliff:g id="APP">%1$s</xliff:g> využívá tato oprávnění: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikace využívají tato oprávnění: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Otevřít aplikaci"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Zrušit"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Nastavení"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Aplikace <xliff:g id="APP">%1$s</xliff:g> v posledních <xliff:g id="TIME">%3$d</xliff:g> min využívá toto oprávnění: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Aplikace <xliff:g id="APPS">%1$s</xliff:g> využívají toto oprávnění: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Aplikace <xliff:g id="APP">%1$s</xliff:g> využívá tato oprávnění: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Zobrazit podrobnosti"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikace používající vaše údaje: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikace používající vaše údaje: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" a "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"fotoaparát"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"poloha"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="few"><xliff:g id="NUM_APPS_1">%d</xliff:g> další aplikace</item>
+ <item quantity="many"><xliff:g id="NUM_APPS_1">%d</xliff:g> další aplikace</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> dalších aplikací</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> dalších aplikací</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c30441b..0a9ca2e 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Annuller"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Område med hjælpemeddelelse"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekræft"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sæt fingeren på fingeraftrykslæseren"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon for fingeraftryk"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Forsøger at finde dig…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Aktiverer…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Lysstyrke"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Roter automatisk"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Underretninger"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Du får ikke længere vist disse underretninger"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Disse underretninger minimeres"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Disse underretninger vises lydløst"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Disse underretninger underretter dig"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Du afviser som regel disse underretninger. \nVil du blive ved med at se dem?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Vil du fortsætte med at se disse underretninger?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop underretninger"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Fortsæt med at vise underretninger"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimer"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Vis lydløst"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsætte med at se underretninger fra denne app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Disse underretninger kan ikke deaktiveres"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Denne app anvender kameraet."</string>
<string name="appops_microphone" msgid="741508267659494555">"Denne app anvender mikrofonen."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Denne app vises over andre apps på din skærm."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Indstillinger"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Gem SysUI-heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> anvender enhedens <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Apps anvender enhedens <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Åbn app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Luk"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Indstillinger"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> har anvendt enhedens <xliff:g id="TYPE">%2$s</xliff:g> i de sidste <xliff:g id="TIME">%3$d</xliff:g> min."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> anvender enhedens <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> anvender enhedens <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Se oplysninger"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App, der anvender din/dit <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps, der anvender din/dit <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" og "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"placering"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> anden app</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andre apps</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 69af8f7..23336de 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Bereich für die Hilfemeldung"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bestätigen"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Berühre den Fingerabdrucksensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerabdruck-Symbol"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Wir suchen nach dir…"</string>
@@ -294,6 +296,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Eingabe"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Wird aktiviert…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Helligkeit"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatisch drehen"</string>
@@ -602,21 +606,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Benachrichtigungen"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Du erhältst diese Benachrichtigungen nicht mehr"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Diese Benachrichtigungen werden minimiert"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Diese Benachrichtigungen werden ohne Ton angezeigt"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Diese Benachrichtigungen werden als Warnungen angezeigt"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Normalerweise schließt du diese Benachrichtigungen. \nSollen sie trotzdem weiter angezeigt werden?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Diese Benachrichtigungen weiterhin anzeigen?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Benachrichtigungen nicht mehr anzeigen"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Weiterhin anzeigen"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimieren"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Ohne Ton anzeigen"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Benachrichtigungen dieser App weiterhin anzeigen?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Diese Benachrichtigungen können nicht deaktiviert werden"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"über <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Diese App verwendet die Kamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Diese App verwendet das Mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Diese App wird über anderen Apps auf dem Bildschirm angezeigt."</string>
@@ -863,16 +871,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Einstellungen"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> verwendet gerade Folgendes: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Apps verwenden gerade Folgendes: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"App öffnen"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Abbrechen"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Einstellungen"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> verwendet seit <xliff:g id="TIME">%3$d</xliff:g> Minuten Folgendes: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> verwenden gerade Folgendes: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> verwendet gerade Folgendes: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Details ansehen"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App, die <xliff:g id="TYPES_LIST">%s</xliff:g> verwendet"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps, die <xliff:g id="TYPES_LIST">%s</xliff:g> verwenden"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" und "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"Kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"Standort"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"Mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> weitere Apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> weitere App</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 67355ef..48f6d36 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Περιοχή μηνυμάτων βοήθειας"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Επιβεβαίωση"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Αναζήτηση για εσάς…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Ήχος"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Ακουστικά"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Είσοδος"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Ενεργοποίηση…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Φωτεινότητα"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Αυτόματη περιστροφή"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Ειδοποιήσεις"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Δεν θα βλέπετε πλέον αυτές τις ειδοποιήσεις"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Αυτές οι ειδοποιήσεις θα ελαχιστοποιηθούν"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Αυτές οι ειδοποιήσεις θα εμφανίζονται σιωπηλά"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Αυτές οι ειδοποιήσεις θα σας ενημερώνουν"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Συνήθως απορρίπτετε αυτές τις ειδοποιήσεις. \nΝα εξακολουθήσουν να εμφανίζονται;"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Να συνεχίσουν να εμφανίζονται αυτές οι ειδοποιήσεις;"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Διακοπή ειδοποιήσεων"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Συνέχιση εμφάνισης"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Ελαχιστοποίηση"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Εμφάνιση σιωπηλά"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Να συνεχίσουν να εμφανίζονται ειδοποιήσεις από αυτήν την εφαρμογή;"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Αδύνατη η απενεργοποίηση αυτών των ειδοποιήσεων"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"μέσω <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Αυτή η εφαρμογή χρησιμοποιεί την κάμερα."</string>
<string name="appops_microphone" msgid="741508267659494555">"Αυτή η εφαρμογή χρησιμοποιεί το μικρόφωνο."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Αυτή η εφαρμογή εμφανίζεται πάνω σε άλλες εφαρμογές στην οθόνη σας."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Ρυθμίσεις"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Το κατάλαβα"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Στιγμ. μνήμης SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> χρησιμοποιεί τις λειτουργίες <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Οι εφαρμογές χρησιμοποιούν τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Άν. εφαρμ."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Ακύρωση"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ΟΚ"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Ρυθμίσεις"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> χρησιμοποιεί τη λειτουργία <xliff:g id="TYPE">%2$s</xliff:g> εδώ και <xliff:g id="TIME">%3$d</xliff:g> λ."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Οι εφαρμογές <xliff:g id="APPS">%1$s</xliff:g> χρησιμοποιούν τη λειτουργία <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> χρησιμοποιεί τις λειτουργίες <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Προβολή λεπτομ."</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Εφαρμογή που χρησιμοποιεί τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Εφαρμογές που χρησιμοποιούν τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" και "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"κάμερα"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"τοποθεσία"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"μικρόφωνο"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> εφαρμογές ακόμα</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> εφαρμογή ακόμα</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 26590be..c9a984b 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Turning on…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-rotate"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Show silently"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Show and alert"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
<string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Settings"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Open app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancel"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Settings"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPE">%2$s</xliff:g> for the last <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> are using your <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"View details"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" and "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"camera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"location"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microphone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 2a59d07..effbb0b 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Turning on…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-rotate"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Show silently"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Show and alert"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
<string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Settings"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Open app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancel"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Settings"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPE">%2$s</xliff:g> for the last <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> are using your <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"View details"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" and "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"camera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"location"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microphone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 26590be..c9a984b 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Turning on…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-rotate"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Show silently"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Show and alert"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
<string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Settings"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Open app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancel"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Settings"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPE">%2$s</xliff:g> for the last <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> are using your <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"View details"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" and "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"camera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"location"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microphone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 26590be..c9a984b 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Turning on…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-rotate"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Show silently"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Show and alert"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
<string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Settings"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Open app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancel"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Settings"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPE">%2$s</xliff:g> for the last <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> are using your <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"View details"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" and "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"camera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"location"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microphone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 35075a7..094aa51 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -112,6 +112,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
+ <string name="biometric_dialog_try_again" msgid="1900185172633183201">"Try again"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -290,6 +291,7 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="4930931771490695395">"Hearing Aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Turning on…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-rotate"</string>
@@ -603,12 +605,16 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <string name="inline_block_button" msgid="8735843688021655065">"Block"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimize"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Show silently"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Show and alert"</string>
+ <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Stay silent"</string>
+ <string name="inline_silent_button_alert" msgid="7961887853830826523">"Alert me"</string>
+ <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Keep alerting"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
<string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -855,16 +861,27 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Settings"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Got it"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <plurals name="ongoing_privacy_chip_multiple_apps" formatted="false" msgid="1406406529558080714">
+ <item quantity="other"><xliff:g id="NUM_APPS_2">%d</xliff:g> apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> app</item>
+ </plurals>
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Open app"</string>
+ <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op" formatted="false" msgid="4871926099254314088">
+ <item quantity="other"><xliff:g id="NUM_APPS_4">%1$d</xliff:g> applications are using your <xliff:g id="TYPE_5">%2$s</xliff:g>.</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%1$d</xliff:g> application is using your <xliff:g id="TYPE_1">%2$s</xliff:g>.</item>
+ </plurals>
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancel"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Okay"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Settings"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPE">%2$s</xliff:g> for the last <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> are using your <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> is using your <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"View details"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps using your <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" and "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"camera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"location"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microphone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 54362c3..4d44e0d 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área de mensajes de ayuda"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor de huellas digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícono de huella digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Autenticando tu rostro…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Auriculares"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrada"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Activando…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brillo"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotación automática"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificaciones"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Ya no verás estas notificaciones"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Se minimizarán estas notificaciones"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Estas notificaciones se mostrarán sin emitir sonido"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Estas notificaciones te enviarán una alerta"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Sueles descartar estas notificaciones. \n¿Quieres seguir recibiéndolas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"¿Quieres seguir viendo estas notificaciones?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Detener notificaciones"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Seguir viendo"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostrar sin emitir sonido"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"No se pueden desactivar estas notificaciones"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"por medio de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Esta app está usando la cámara."</string>
<string name="appops_microphone" msgid="741508267659494555">"Esta app está usando el micrófono."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Esta app se muestra sobre otras apps en la pantalla."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Configuración"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Entendido"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Volcar pila de SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> está usando tu <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Hay aplicaciones que están usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Abrir app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancelar"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Aceptar"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Ajustes"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> lleva <xliff:g id="TIME">%3$d</xliff:g> min usando tu <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> están usando tu <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> está usando tu <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ver detalles"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Una app está usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps que están usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" y "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"cámara"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ubicación"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"micrófono"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> apps más</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> app más</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 7fb2b8e..9c09196 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área de mensaje de ayuda"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor de huellas digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icono de huella digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscando tu cara…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Auriculares"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrada"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Activando…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brillo"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Girar automáticamente"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Normalmente ignoras estas notificaciones. \n¿Quieres seguir viéndolas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"¿Quieres seguir viendo estas notificaciones?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Detener las notificaciones"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Seguir mostrando"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostrar en silencio"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Mostrar y sonar"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta aplicación?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Estas notificaciones no se pueden desactivar"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"mediante <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Esta aplicación está usando la cámara."</string>
<string name="appops_microphone" msgid="741508267659494555">"Esta aplicación está usando el micrófono."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Esta aplicación se está mostrando sobre otras aplicaciones en tu pantalla."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Ajustes"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Entendido"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Volcar pila de SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> está usando tu <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Hay aplicaciones que usan tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Abrir app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancelar"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Aceptar"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Ajustes"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> está usando tu <xliff:g id="TYPE">%2$s</xliff:g> desde hace <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> están usando tu <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> está usando tu <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ver detalles"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicación que usa tu <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicaciones que usan tu <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" y "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"cámara"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ubicación"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"micrófono"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplicaciones más</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplicación más</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 65a42a6..8c755b9 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Tühista"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Abisõnumi ala"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Kinnita"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Puudutage sõrmejäljeandurit"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Sõrmejälje ikoon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Otsitakse teid …"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Heli"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Peakomplekt"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Sisend"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Sisselülitamine …"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Heledus"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automaatne pööramine"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Märguanded"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Te ei näe enam neid märguandeid"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Need märguanded minimeeritakse"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Need märguanded kuvatakse vaikselt"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Need märguanded teavitavad teid heliga"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Tavaliselt loobute nendest märguannetest. \nKas soovite neid jätkuvalt näidata?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Kas soovite nende märguannete kuvamist jätkata?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Peata märguanded"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Jätka kuvamist"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimeeri"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Kuva vaikselt"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Kas jätkata selle rakenduse märguannete kuvamist?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Neid märguandeid ei saa välja lülitada"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> kaudu"</string>
<string name="appops_camera" msgid="8100147441602585776">"See rakendus kasutab kaamerat."</string>
<string name="appops_microphone" msgid="741508267659494555">"See rakendus kasutab mikrofoni."</string>
<string name="appops_overlay" msgid="6165912637560323464">"See rakendus kuvatakse teie ekraanil muude rakenduste peal."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Seaded"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Selge"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> kasutab järgmisi: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Rakendused kasutavad järgmisi: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Ava rakendus"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Tühista"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Seaded"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> on viimased <xliff:g id="TIME">%3$d</xliff:g> minutit kasutanud järgmist: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> kasutavad järgmist: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> kasutab järgmisi: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Kuva üksikasjad"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Rakendus, mis kasutab üksusi <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Rakendused, mis kasutavad üksusi <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ja "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kaamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"asukoht"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Veel <xliff:g id="NUM_APPS_1">%d</xliff:g> rakendust</item>
+ <item quantity="one">Veel <xliff:g id="NUM_APPS_0">%d</xliff:g> rakendus</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 97ba5b9..51a7935 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Utzi"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Laguntza-mezuaren eremua"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Berretsi"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sakatu hatz-marken sentsorea"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Hatz-markaren ikonoa"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Zure bila…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audioa"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Entzungailua"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Sarrera"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Aktibatzen…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Distira"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Biratze automatikoa"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Baztertu egin ohi dituzu jakinarazpen hauek. \nHaiek erakusten jarraitzea nahi duzu?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Jakinarazpenak erakusten jarraitzea nahi duzu?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Blokeatu jakinarazpenak"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Jarraitu erakusten"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizatu"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Erakutsi soinurik egin gabepen"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Erakutsi eta egin soinua"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Aplikazio honen jakinarazpenak erakusten jarraitzea nahi duzu?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Jakinarazpen hauek ezin dira desaktibatu"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren bidez"</string>
<string name="appops_camera" msgid="8100147441602585776">"Kamera erabiltzen ari da aplikazioa."</string>
<string name="appops_microphone" msgid="741508267659494555">"Mikrofonoa erabiltzen ari da aplikazioa."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Pantailako beste aplikazioen gainean agertzen da aplikazioa."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Ezarpenak"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Ados"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="TYPES_LIST">%2$s</xliff:g> erabiltzen ari da."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikazio batzuk <xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari dira."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Ireki aplikazioa"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Utzi"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Ados"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Ezarpenak"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> aplikazioak <xliff:g id="TYPE">%2$s</xliff:g> erabili du azken <xliff:g id="TIME">%3$d</xliff:g> minutuetan"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> erabiltzen ari dira"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="TYPES_LIST">%2$s</xliff:g> erabiltzen ari da"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ikusi datuak"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari den aplikazioa"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari diren aplikazioak"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" eta "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"kokapena"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofonoa"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">beste <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikazio</item>
+ <item quantity="one">beste <xliff:g id="NUM_APPS_0">%d</xliff:g> aplikazio</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 049513a..e31a07b 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"بخش پیام راهنما"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأیید"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"حسگر اثر انگشت را لمس کنید"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"نماد اثر انگشت"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"درحال جستجوی شما…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"هدست"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ورودی"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"روشن کردن…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"روشنایی"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"چرخش خودکار"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"اعلانها"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"دیگر این اعلانها را نخواهید دید"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"این اعلانها کوچک خواهد شد"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"این اعلانها بهصورت بیصدا نشان داده میشود"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"این اعلانها به شما هشدار خواهند داد"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"معمولاً این اعلانها را رد میکنید. \nهمچنان نشان داده شود؟"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"نمایش این اعلانها ادامه یابد؟"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"توقف اعلانها"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"همچنان نشان داده شود"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"کوچک کردن"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"نمایش بهصورت بیصدا"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"نمایش اعلان از این برنامه ادامه یابد؟"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"نمیتوان این اعلانها را خاموش کرد"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"ازطریق <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"این برنامه از دوربین استفاده میکند."</string>
<string name="appops_microphone" msgid="741508267659494555">"این برنامه از میکروفون استفاده میکند."</string>
<string name="appops_overlay" msgid="6165912637560323464">"این برنامه روی برنامههای دیگر در صفحهنمایش نشان داده میشود."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"تنظیمات"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"متوجه شدم"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> از <xliff:g id="TYPES_LIST">%2$s</xliff:g> شما استفاده میکند."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"برنامهها از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکنند."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"باز کردن برنامه"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"لغو"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"خوب"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"تنظیمات"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> از <xliff:g id="TYPE">%2$s</xliff:g> در <xliff:g id="TIME">%3$d</xliff:g> دقیقه آخر استفاده میکند"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> از <xliff:g id="TYPE">%2$s</xliff:g> شما استفاده میکند"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> از <xliff:g id="TYPES_LIST">%2$s</xliff:g> شما استفاده میکند"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"مشاهده جزئیات"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"برنامهای که از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکند"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"برنامههایی که از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکنند"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"، "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" و "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"دوربین"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"مکان"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"میکروفون"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> برنامه دیگر</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> برنامه دیگر</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 277ef5d..d4e59ae 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Ohjeviestialue"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Vahvista"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Kosketa sormenjälkitunnistinta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Sormenjälkikuvake"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Etsitään kasvoja…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Ääni"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Syöttölaite"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Otetaan käyttöön…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Kirkkaus"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automaattinen kääntö"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Ilmoitukset"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Et näe näitä ilmoituksia enää"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Nämä ilmoitukset pienennetään"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Nämä ilmoitukset näytetään ilman ääniä"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Nämä ilmoitukset hälyttävät"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Hylkäät yleensä nämä ilmoitukset. \nHaluatko, että niitä näytetään myös jatkossa?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Jatketaanko näiden ilmoitusten näyttämistä?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Lopeta ilmoitukset"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Jatka näyttämistä"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Pienennä"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Näytä ilman ääntä"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Jatketaanko ilmoitusten näyttämistä tästä sovelluksesta?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Näitä ilmoituksia ei voi poistaa käytöstä"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Tämä sovellus käyttää kameraa."</string>
<string name="appops_microphone" msgid="741508267659494555">"Tämä sovellus käyttää mikrofonia."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Tämä sovellus näkyy näytöllä muiden sovellusten päällä."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Asetukset"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Selvä"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Luo SysUI-keon vedos"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> käyttää ominaisuuksia (<xliff:g id="TYPES_LIST">%2$s</xliff:g>)."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"<xliff:g id="TYPES_LIST">%s</xliff:g> ovat sovellusten käytössä."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Avaa"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Peruuta"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Asetukset"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> on käyttänyt ominaisuutta (<xliff:g id="TYPE">%2$s</xliff:g>) <xliff:g id="TIME">%3$d</xliff:g> minuutin ajan."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> käyttävät ominaisuutta (<xliff:g id="TYPE">%2$s</xliff:g>)."</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> käyttää ominaisuuksia (<xliff:g id="TYPES_LIST">%2$s</xliff:g>)."</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Näytä tiedot"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Sovellus, jolla on <xliff:g id="TYPES_LIST">%s</xliff:g> ‑käyttöoikeus"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Sovellukset, joilla on <xliff:g id="TYPES_LIST">%s</xliff:g> ‑käyttöoikeus"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ja "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"sijainti"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofoni"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> muuta sovellusta</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> muu sovellus</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 905af5b..31cc6d3 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zone de message d\'aide"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmer"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touchez le capteur d\'empreintes digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icône d\'empreinte digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Écouteurs"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrée"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Activation en cours…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosité"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotation automatique"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Vous ne verrez plus ces notifications"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ces notifications seront réduites"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ces notifications s\'afficheront en silence"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ces notifications vous alerteront"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Vous ignorez habituellement ces notifications. \nSouhaitez-vous continuer à les afficher?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuer à afficher ces notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Arrêter les notifications"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continuer à afficher"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Réduire"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Afficher en silence"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer à afficher les notifications de cette application?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Cette application utilise l\'appareil photo."</string>
<string name="appops_microphone" msgid="741508267659494555">"Cette application utilise le microphone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Cette application superpose du contenu par-dessus d\'autres applications à l\'écran."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Paramètres"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Capturer mémoire SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> utilise votre <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Ouv. appli"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Annuler"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Paramètres"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> utilise votre <xliff:g id="TYPE">%2$s</xliff:g> depuis <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> utilisent votre <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> utilise votre <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Afficher détails"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Application qui utilise votre <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Applications qui utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" et "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"appareil photo"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"position"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microphone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> autre application</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> autres applications</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 88e5f6cd..db6ffd9 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zone de message d\'aide"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmer"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Appuyez sur le lecteur d\'empreinte digitale"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icône d\'empreinte digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Casque"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrée"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Activation…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosité"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotation automatique"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Vous ne recevrez plus ces notifications"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ces notifications seront réduites"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ces notifications seront affichées en silence"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ces notifications vous alerteront avec un son"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Vous ignorez généralement ces notifications. \nSouhaitez-vous continuer de les recevoir ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuer d\'afficher ces notifications ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Arrêter les notifications"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continuer d\'afficher les notifications"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Réduire"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Affichage silencieux"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer d\'afficher les notifications de cette application ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Cette application utilise la caméra."</string>
<string name="appops_microphone" msgid="741508267659494555">"Cette application utilise le micro."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Cette application se superpose aux autres applications sur l\'écran."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Paramètres"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Copier mémoire SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> utilise votre <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Ouvrir appli"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Annuler"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Paramètres"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> utilise votre <xliff:g id="TYPE">%2$s</xliff:g> depuis <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> utilisent votre <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> utilise votre <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Voir les détails"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Application utilisant votre/vos <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Applications utilisant votre/vos <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" et "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"appareil photo"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"position"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"micro"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> autre application</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> autres applications</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 999c1ca..5dccba3 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área de mensaxes de axuda"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca o sensor de impresión dixital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona de impresión dixital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscándote…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Auriculares"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrada"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Activando…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brillo"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Xirar automaticamente"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificacións"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Deixarás de ver estas notificacións"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Minimizaranse estas notificacións"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Estas notificacións mostraranse en silencio"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Estas notificacións mostraranse con son"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Ignoras estas notificacións a miúdo. \nQueres seguir recibíndoas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Queres seguir mostrando estas notificacións?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Deter notificacións"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando notificacións"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostrar en silencio"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Queres seguir mostrando as notificacións desta aplicación?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Non se poden desactivar estas notificacións"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"mediante <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Esta aplicación está utilizando a cámara."</string>
<string name="appops_microphone" msgid="741508267659494555">"Esta aplicación está utilizando o micrófono."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Esta aplicación móstrase sobre outras aplicacións da pantalla."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Configuración"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"De acordo"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Baleirar mont. SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> está utilizando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Hai aplicacións que están utilizando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Abrir app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancelar"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Aceptar"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Axustes"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> está utilizando <xliff:g id="TYPE">%2$s</xliff:g> desde hai <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> están utilizando <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> está utilizando <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ver detalles"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicación que utiliza o seguinte: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicacións que utilizan o seguinte: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" e "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"a cámara"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"a localiz."</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"o micrófono"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplicacións máis</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplicación máis</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index ecaa321..1e6e733 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"રદ કરો"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"સહાય સંદેશનું ક્ષેત્ર"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"કન્ફર્મ કરો"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ફિંગરપ્રિન્ટના સેન્સરને સ્પર્શ કરો"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ફિંગરપ્રિન્ટનું આઇકન"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"તમારા માટે શોધી રહ્યાં છે..."</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ઑડિઓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"હૅડસેટ"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ઇનપુટ"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ચાલુ કરી રહ્યાં છીએ…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"તેજ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"આપમેળે ફેરવો"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"નોટિફિકેશનો"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"તમને હવેથી આ નોટિફિકેશન દેખાશે નહીં"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"આ બધા નોટિફિકેશન નાના કરવામાં આવશે"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"આ બધા નોટિફિકેશન સાઇલન્ટલી બતાવવામાં આવશે"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"આ બધા નોટિફિકેશન તમને અલર્ટ કરશે"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"તમે સામાન્ય રીતે આ નોટીફિકેશનને છોડી દો છો. \nતેમને બતાવવાનું ચાલુ રાખીએ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"આ નોટિફિકેશન બતાવવાનું ચાલુ રાખીએ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"નોટિફિકેશન બંધ કરો"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"બતાવવાનું ચાલુ રાખો"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"નાનું કરો"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"સાઇલન્ટલી બતાવો"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"આ ઍપમાંથી નોટિફિકેશન બતાવવાનું ચાલુ રાખીએ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"આ નોટિફિકેશન બંધ કરી શકશો નહીં"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> મારફતે"</string>
<string name="appops_camera" msgid="8100147441602585776">"આ ઍપ કૅમેરાનો ઉપયોગ કરી રહી છે."</string>
<string name="appops_microphone" msgid="741508267659494555">"આ ઍપ માઇક્રોફોનનો ઉપયોગ કરી રહી છે."</string>
<string name="appops_overlay" msgid="6165912637560323464">"આ ઍપ તમારી સ્ક્રીન પરની અન્ય ઍપની ઉપર પ્રદર્શિત થઈ રહી છે."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"સેટિંગ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"સમજાઈ ગયું"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ઍપ તમારા <xliff:g id="TYPES_LIST">%2$s</xliff:g>નો ઉપયોગ કરી રહી છે."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ઍપ્લિકેશન તમારા <xliff:g id="TYPES_LIST">%s</xliff:g>નો ઉપયોગ કરી રહી છે."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ઍપ ખોલો"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"રદ કરો"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ઓકે"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"સેટિંગ"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"છેલ્લી <xliff:g id="TIME">%3$d</xliff:g> મિનિટથી <xliff:g id="APP">%1$s</xliff:g> ઍપ તમારા <xliff:g id="TYPE">%2$s</xliff:g>નો ઉપયોગ કરી રહી છે"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ઍપ તમારા <xliff:g id="TYPE">%2$s</xliff:g>નો ઉપયોગ કરી રહી છે"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> અૅપ તમારા <xliff:g id="TYPES_LIST">%2$s</xliff:g>નો ઉપયોગ કરી રહી છે"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"વિગતો જુઓ"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"અૅપ તમારા <xliff:g id="TYPES_LIST">%s</xliff:g>નો ઉપયોગ કરી રહી છે"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"અૅપ તમારા <xliff:g id="TYPES_LIST">%s</xliff:g>નો ઉપયોગ કરી રહી છે"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" અને "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"કૅમેરા"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"સ્થાન"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"માઇક્રોફોન"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> અન્ય ઍપ</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> અન્ય ઍપ</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index c1cb317..57709aa 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"सहायता का मैसेज दिखाने की जगह"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि करें"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फ़िंगरप्रिंट सेंसर को छुएं"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फ़िंगरप्रिंट आइकॉन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"आपको पहचान रहा है…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ऑडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"हेडसेट"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"इनपुट"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ब्लूटूथ चालू हो रहा है…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"स्क्रीन की रोशनी"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"स्वत: घुमाएं"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"अब आपको ये सूचनाएं दिखाई नहीं देंगी"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"इन सूचनाओं को छोटा कर दिया जाएगा"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ये सूचनाएं बिना आवाज़ के दिखाई जाएंगी"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ये सूचनाएं आपको अलर्ट करेंगी"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"अाप अक्सर इन सूचनाओं को खारिज कर देते हैं. \nआगे भी इन्हें देखना जारी रखना चाहते हैं?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ये सूचनाएं दिखाना जारी रखें?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"सूचनाएं दिखाना बंद करें"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"दिखाना जारी रखें"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"सूचनाएं छोटी करें"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"बिना आवाज़ के दिखाएं"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"इस ऐप्लिकेशन से जुड़ी सूचनाएं दिखाना जारी रखें?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ये सूचनाएं दिखाया जाना बंद नहीं किया जा सकता"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> के ज़रिए"</string>
<string name="appops_camera" msgid="8100147441602585776">"यह ऐप्लिकेशन कैमरे का इस्तेमाल कर रहा है."</string>
<string name="appops_microphone" msgid="741508267659494555">"यह ऐप्लिकेशन माइक्रोफ़ोन का इस्तेमाल कर रहा है."</string>
<string name="appops_overlay" msgid="6165912637560323464">"यह ऐप्लिकेशन आपकी स्क्रीन पर इस्तेमाल हो रहे दूसरे ऐप्लिकेशन के ऊपर दिखाया जा रहा है."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"सेटिंग"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ठीक है"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> आपकी <xliff:g id="TYPES_LIST">%2$s</xliff:g> का इस्तेमाल कर रहा है."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ऐप्लिकेशन आपकी <xliff:g id="TYPES_LIST">%s</xliff:g> का इस्तेमाल कर रहे हैं."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ऐप खोलें"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"रद्द करें"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ठीक है"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"सेटिंग"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"पिछले <xliff:g id="TIME">%3$d</xliff:g> मि. से <xliff:g id="APP">%1$s</xliff:g> आपकी <xliff:g id="TYPE">%2$s</xliff:g> का इस्तेमाल कर रहा है"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> आपकी <xliff:g id="TYPE">%2$s</xliff:g> का इस्तेमाल कर रहे हैं"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> आपकी <xliff:g id="TYPES_LIST">%2$s</xliff:g> का इस्तेमाल कर रहा है"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"विवरण देखें"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> का इस्तेमाल कर रहा ऐप्लिकेशन"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"आपके <xliff:g id="TYPES_LIST">%s</xliff:g> का इस्तेमाल कर रहे ऐप्लिकेशन"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" और "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"कैमरा"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"जगह"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"माइक्रोफ़ोन"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> दूसरे ऐप्लिकेशन</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> दूसरे ऐप्लिकेशन</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 901e2fe..40d9932 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Odustani"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Područje poruke za pomoć"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdi"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor otiska prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otiska prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
@@ -291,6 +293,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Slušalice"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Unos"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Uključivanje…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Svjetlina"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatsko izmjenjivanje"</string>
@@ -606,12 +610,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Obično odbacujete te obavijesti. \nŽelite li da se nastave prikazivati?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Želite li da se obavijesti nastave prikazivati?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Zaustavi obavijesti"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Nastavi prikazivati"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimiziraj"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Prikaži tiho"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Prikaži uz zvučni signal"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obavijesti te aplikacije nastave prikazivati?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Te se obavijesti ne mogu isključiti"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"putem aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ova aplikacija upotrebljava kameru."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ova aplikacija upotrebljava mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ova se aplikacija prikazuje preko drugih aplikacija na zaslonu."</string>
@@ -860,16 +872,22 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Postavke"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Shvaćam"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Izdvoji mem. SysUI-a"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> upotrebljava <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikacije upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Otvori aplikaciju"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Odustani"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"U redu"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Postavke"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> upotrebljava <xliff:g id="TYPE">%2$s</xliff:g> unatrag <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Aplikacije <xliff:g id="APPS">%1$s</xliff:g> upotrebljavaju <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> upotrebljava <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Prikaži detalje"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacije koje upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacije koje upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" i "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"fotoaparat"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokaciju"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">Još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacija</item>
+ <item quantity="few">Još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacije</item>
+ <item quantity="other">Još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacija</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7a5e026..e1cdc0b 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Mégse"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Súgószöveg területe"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Megerősítés"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Érintse meg az ujjlenyomat-érzékelőt"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ujjlenyomat ikonja"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Keresem az Ön arcát…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Hang"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Bevitel"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Bekapcsolás…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Fényerő"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatikus elforgatás"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Értesítések"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Többé nem jelennek meg ezek az értesítések"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ezek az értesítések kis méretben jelennek meg"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Az ilyen értesítések hangjelzés nélkül jelennek meg"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Az ilyen értesítések riasztást küldenek"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Általában elveti ezeket az értesítéseket.\nSzeretné, hogy továbbra is megjelenjenek?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Továbbra is megjelenjenek ezek az értesítések?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Értesítések letiltása"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Megjelenítés továbbra is"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Kis méret"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Megjelenítés hang nélkül"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Továbbra is megjelenjenek az alkalmazás értesítései?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ezeket az értesítéseket nem lehet kikapcsolni"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás használatával"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ez az alkalmazás használja a kamerát."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ez az alkalmazás használja a mikrofont."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ez az alkalmazás a képernyőn lévő egyéb alkalmazások előtt jelenik meg."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Beállítások"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Értem"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI-memória-kiírás"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"A(z) <xliff:g id="APP">%1$s</xliff:g> használja a következőket: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Több alkalmazás használja a következőket: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Megnyitás"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Mégse"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Beállítás"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"A(z) <xliff:g id="APP">%1$s</xliff:g> az elmúlt <xliff:g id="TIME">%3$d</xliff:g> percben használta a következőt: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"A(z) <xliff:g id="APPS">%1$s</xliff:g> használja a következőt: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"A(z) <xliff:g id="APP">%1$s</xliff:g> használja a következőt: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Részletek"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"A következőket használó alkalmazás: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"A következőt használó alkalmazások: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" és "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"helyadatok"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> további alkalmazás</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> további alkalmazás</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index da36e62..f5921dc 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Օգնության հաղորդագրության դաշտ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Հաստատել"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Հպեք մատնահետքերի սկաներին"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Մատնահետքի պատկերակ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Դեմքի ճանաչում…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Աուդիո"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Ականջակալ"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Մուտքագրում"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Միացում…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Պայծառություն"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Ինքնապտտում"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Ծանուցումներ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Դուք այլևս չեք ստանա այս ծանուցումները"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Այս ծանուցումները կծալվեն:"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Այս ծանուցումները կցուցադրվեն առանց ձայնի"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Այս ծանուցումները կցուցադրվեն զգուշացումով"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Դուք սովորաբար փակում եք այս ծանուցումները: \nՇարունակե՞լ ցուցադրել դրանք:"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Ցուցադրե՞լ այս ծանուցումները։"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Չցուցադրել ծանուցումներ"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Ցուցադրել"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Ծալել"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Ցույց տալ անձայն"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Ցուցադրե՞լ ծանուցումներ այս հավելվածից։"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Այս ծանուցումները հնարավոր չէ անջատել"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի միջոցով"</string>
<string name="appops_camera" msgid="8100147441602585776">"Այս հավելվածն օգտագործում է տեսախցիկը:"</string>
<string name="appops_microphone" msgid="741508267659494555">"Այս հավելվածն օգտագործում է խոսափողը:"</string>
<string name="appops_overlay" msgid="6165912637560323464">"Այս հավելվածը ցուցադրվում է մյուս հավելվածների վրայից:"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Կարգավորումներ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Եղավ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> հավելվածն օգտագործում է ձեր <xliff:g id="TYPES_LIST">%2$s</xliff:g>:"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Հավելվածներն օգտագործում են ձեր <xliff:g id="TYPES_LIST">%s</xliff:g>:"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Բացել հավելվածը"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Չեղարկել"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Փակել"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Պարամետրեր"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> հավելվածն օգտագործում է ձեր <xliff:g id="TYPE">%2$s</xliff:g> վերջին <xliff:g id="TIME">%3$d</xliff:g> րոպեի ընթացքում"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> հավելվածներն օգտագործում են ձեր <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> հավելվածն օգտագործում է ձեր <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Մանրամասն"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Հավելված, որն օգտագործում է <xliff:g id="TYPES_LIST">%s</xliff:g> ձեր սարքում"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Հավելվածներ, որոնք օգտագործում են <xliff:g id="TYPES_LIST">%s</xliff:g> ձեր սարքում"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" և "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"տեսախցիկը"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"վայրը"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"խոսափողը"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">Եվս <xliff:g id="NUM_APPS_1">%d</xliff:g> հավելված</item>
+ <item quantity="other">Եվս <xliff:g id="NUM_APPS_1">%d</xliff:g> հավելված</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index bb0a725..06609e3 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Area pesan bantuan"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Konfirmasi"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sentuh sensor sidik jari"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon sidik jari"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari wajah Anda…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Masukan"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Mengaktifkan…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Kecerahan"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotasi otomatis"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Anda biasanya menutup notifikasi ini. \nTerus tampilkan?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Terus tampilkan notifikasi ini?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Hentikan notifikasi"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Terus tampilkan"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Perkecil"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Tampilkan tanpa suara"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Tampilkan dan ingatkan"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tampilkan notifikasi dari aplikasi ini?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Notifikasi ini tidak dapat dinonaktifkan"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"melalui <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Aplikasi ini sedang menggunakan kamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Aplikasi ini sedang menggunakan mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Aplikasi ini ditampilkan di atas aplikasi lain di layar."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Setelan"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Hapus Heap SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikasi menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Buka apl"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Batal"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Oke"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Setelan"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> menggunakan <xliff:g id="TYPE">%2$s</xliff:g> selama <xliff:g id="TIME">%3$d</xliff:g> menit terakhir"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> menggunakan <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Lihat detail"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikasi yang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> Anda"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikasi yang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> Anda"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" dan "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokasi"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplikasi lain</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplikasi lain</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 3d01b94..83c75e4 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Hætta við"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Svæði hjálparskilaboða"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Staðfesta"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Snertu fingrafaralesarann"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingrafaratákn"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Leitar að þér ..."</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Hljóð"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Höfuðtól"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Inntak"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Kveikir…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Birtustig"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Sjálfvirkur snúningur"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Tilkynningar"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Þú munt ekki sjá þessar tilkynningar aftur"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Þessar tilkynningar verða faldar"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Þessar tilkynningar verða birtar án hljóðs"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Þessar tilkynningar munu láta þig vita"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Þú hunsar yfirleitt þessar tilkynningar. \nViltu halda áfram að fá þær?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Sýna áfram þessar tilkynningar?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stöðva tilkynningar"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Sýna áfram"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minnka"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Sýna án hljóðs"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Sýna áfram tilkynningar frá þessu forriti?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ekki er hægt að slökkva á þessum tilkynningum"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"með <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Þetta forrit er að nota myndavélina."</string>
<string name="appops_microphone" msgid="741508267659494555">"Þetta forrit er að nota hljóðnemann."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Þetta forrit er að birta efni yfir öðrum forritum á skjánum þínum."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Stillingar"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Ég skil"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Vista SysUI-gögn"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> er að nota <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Forrit eru að nota <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Opna forr."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Hætta við"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Í lagi"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Stillingar"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> hefur verið að nota <xliff:g id="TYPE">%2$s</xliff:g> í <xliff:g id="TIME">%3$d</xliff:g> mín."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> er að nota <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> er að nota <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Sjá nánar"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Forrit sem nota <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Forrit sem nota <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" og "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"myndavél"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"staðsetning"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"hljóðnemi"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> forrit í viðbót</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> forrit í viðbót</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index b1f9230..4c512fe 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Annulla"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Area dei messaggi di assistenza"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confermo"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Tocca il sensore di impronte digitali"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona dell\'impronta digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"In attesa del volto…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Auricolare"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Ingresso"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Attivazione…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosità"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotazione automatica"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"In genere ignori queste notifiche. \nVuoi continuare a riceverle?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuare a ricevere queste notifiche?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Interrompi la ricezione di notifiche"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continua a mostrare"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Riduci a icona"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostra silenziosamente"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Mostra e avvisa"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuare a ricevere notifiche da questa app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Queste notifiche non possono essere disattivate"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"tramite <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Questa app sta utilizzando la fotocamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Questa app sta utilizzando il microfono."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Questa app è visualizzata sopra altre app sullo schermo."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Impostazioni"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Esegui dump heap SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Apri app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Annulla"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Impostazioni"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPE">%2$s</xliff:g> da <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Le app <xliff:g id="APPS">%1$s</xliff:g> stanno usando <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Vedi dettagli"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App che usa <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"App che utilizzano <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" e "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"Fotocamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"luogo"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"un microfono"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Altre <xliff:g id="NUM_APPS_1">%d</xliff:g> app</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> altra app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 70ab947..37fb1f7 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"אזור הודעת עזרה"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"אישור"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"יש לגעת בחיישן טביעות האצבע"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"סמל טביעת אצבע"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"מחפש אותך…"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"אודיו"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"אוזניות"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"קלט"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ההפעלה מתבצעת…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"בהירות"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"סיבוב אוטומטי"</string>
@@ -609,12 +613,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"הודעות אלה בדרך כלל נדחות על ידיך. \nלהמשיך להציג אותן?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"שנמשיך להציג לך את ההודעות האלה?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"לא, אל תמשיכו"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"כן, המשיכו"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"מזעור"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"הצגה ללא צליל"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"הצגה ושליחת התראה"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"שנמשיך להציג לך הודעות מהאפליקציה הזאת?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"לא ניתן לכבות את ההודעות האלה"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"באמצעות <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"האפליקציה הזו משתמשת במצלמה."</string>
<string name="appops_microphone" msgid="741508267659494555">"האפליקציה הזו משתמשת במיקרופון."</string>
<string name="appops_overlay" msgid="6165912637560323464">"האפליקציה הזו מוצגת מעל אפליקציות אחרות במסך."</string>
@@ -865,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"הגדרות"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"הבנתי"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"ערימת Dump SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> משתמשת ב<xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"אפליקציות משתמשות ב<xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"לאפליקציה"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ביטול"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"אישור"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"הגדרות"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> משתמשת ב<xliff:g id="TYPE">%2$s</xliff:g> ב-<xliff:g id="TIME">%3$d</xliff:g> הדקות האחרונות"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> משתמשות ב<xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> משתמשת ב<xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"הצגת פרטים"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"האפליקציה משתמשת ב<xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"אפליקציות משתמשות ב<xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" וגם "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"מצלמה"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"מיקום"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"מיקרופון"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="two"><xliff:g id="NUM_APPS_1">%d</xliff:g> אפליקציות נוספות</item>
+ <item quantity="many"><xliff:g id="NUM_APPS_1">%d</xliff:g> אפליקציות נוספות</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> אפליקציות נוספות</item>
+ <item quantity="one">אפליקציה נוספת (<xliff:g id="NUM_APPS_0">%d</xliff:g>)</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index bd5fb37..519d333 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ヘルプ メッセージ領域"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"指紋認証センサーをタップしてください"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋アイコン"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"顔を認証しています…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"オーディオ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ヘッドセット"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"入力"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ON にしています…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"画面の明るさ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自動回転"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"今後、この通知は表示されません"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"通知を最小化します"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"これらの通知はサイレント モードで表示されます"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"これらの通知はアラートとして送信されます"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"通常、この通知はスワイプして非表示にしています。\n今後も表示しますか?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"この通知を今後も表示しますか?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"通知を表示しない"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"今後も表示する"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"サイレント モードで表示"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"このアプリからの通知を今後も表示しますか?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"この通知を OFF にすることはできません"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> 経由"</string>
<string name="appops_camera" msgid="8100147441602585776">"このアプリはカメラを使用しています。"</string>
<string name="appops_microphone" msgid="741508267659494555">"このアプリはマイクを使用しています。"</string>
<string name="appops_overlay" msgid="6165912637560323464">"このアプリは画面上で他のアプリの上に重ねて表示されます。"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"設定"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ヒープのダンプ"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g>は<xliff:g id="TYPES_LIST">%2$s</xliff:g>を使用しています。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"アプリは<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しています。"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"アプリを開く"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"キャンセル"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"設定"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g>は過去 <xliff:g id="TIME">%3$d</xliff:g> 分間に<xliff:g id="TYPE">%2$s</xliff:g>を使用しています"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g>は<xliff:g id="TYPE">%2$s</xliff:g>を使用しています"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g>は<xliff:g id="TYPES_LIST">%2$s</xliff:g>を使用しています"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"詳細を表示"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しているアプリ"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しているアプリ"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"、 "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" 、 "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"カメラ"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"現在地情報"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"マイク"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">他 <xliff:g id="NUM_APPS_1">%d</xliff:g> 個のアプリ</item>
+ <item quantity="one">他 <xliff:g id="NUM_APPS_0">%d</xliff:g> 個のアプリ</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 5e2454c..50a25bd 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"დამხმარე შეტყობინების არე"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"დადასტურება"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"შეეხეთ თითის ანაბეჭდის სენსორს"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"თითის ანაბეჭდის ხატულა"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"მიმდინარეობს თქვენი ძიება…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"აუდიო"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ყურსაცვამი"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"შეყვანა"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ირთვება…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"სიკაშკაშე"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ავტოროტაცია"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"შეტყობინებები"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ამ შეტყობინებებს აღარ დაინახავთ"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"შეტყობინებები ჩაიკეცება"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ეს შეტყობინებები გამოჩნდება უხმოდ"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"თქვენ მიიღებთ გაფრთხილებას ამ შეტყობინებების შესახებ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"როგორც წესი, თქვენ ასეთ შეტყობინებებს ხურავთ. \nგსურთ მათი ჩვენების გაგრძელება?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"გაგრძელდეს ამ შეტყობინებათა ჩვენება?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"შეტყობინებების შეწყვეტა"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"ჩვენების გაგრძელება"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ჩაკეცვა"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"უხმოდ ჩვენება"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"გაგრძელდეს შეტყობინებათა ჩვენება ამ აპიდან?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ამ შეტყობინებათა გამორთვა ვერ მოხერხდება"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>-დან"</string>
<string name="appops_camera" msgid="8100147441602585776">"ეს აპი იყენებს კამერას."</string>
<string name="appops_microphone" msgid="741508267659494555">"ეს აპი იყენებს მიკროფონს."</string>
<string name="appops_overlay" msgid="6165912637560323464">"ეს აპი თქვენს ეკრანზე ფარავს სხვა აპებს."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"პარამეტრები"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"გასაგებია"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI გროვის გამოტანა"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g>-ის მიერ გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"აპლიკაციების მიერ გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"აპის გახსნა"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"გაუქმება"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"კარგი"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"პარამეტრები"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g>-ის მიერ გამოიყენება თქვენი <xliff:g id="TYPE">%2$s</xliff:g> ბოლო <xliff:g id="TIME">%3$d</xliff:g> წუთის განმავლობაში"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g>-ის მიერ გამოიყენება თქვენი <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g>-ის მიერ გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"დეტალების ნახვა"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"აპი, რომლის მიერაც გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"აპები, რომელთა მიერაც გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" და "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"კამერა"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"მდებარეობა"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"მიკროფონი"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> სხვა აპი</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> სხვა აპი</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index f990be8..b7e02a2 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Анықтама хабары аумағы"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Растау"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Саусақ ізін оқу сканерін түртіңіз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Саусақ ізі белгішесі"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Бет ізделуде…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Aудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Гарнитура"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Кіріс"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Қосылуда…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Жарықтығы"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматты түрде бұру"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Хабарландырулар"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Хабарландырулар бұдан былай көрсетілмейді"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Хабарландырулар жасырылады"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Бұл хабарландырулар дыбыссыз көрсетіледі"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Бұл хабарландырулар сізді ескертеді"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Әдетте хабарландыруларды көрмейсіз. \nОлар көрсетілсін бе?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Хабарландырулар көрсетілсін бе?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Хабарландыруларға тыйым салу"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Көрсету"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Жасыру"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Дыбыссыз көрсету"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Осы қолданбаның хабарландырулары көрсетілсін бе?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Хабарландыруларды өшіру мүмкін емес"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> арқылы"</string>
<string name="appops_camera" msgid="8100147441602585776">"Бұл қолданба камераны пайдалануда."</string>
<string name="appops_microphone" msgid="741508267659494555">"Бұл қолданба микрофонды пайдалануда."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Бұл қолданба экранда басқа қолданбалардың үстінен көрсетіліп тұр."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Параметрлер"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Түсінікті"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> қолданбасында <xliff:g id="TYPES_LIST">%2$s</xliff:g> пайдалануда."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Қолданбаларда <xliff:g id="TYPES_LIST">%s</xliff:g> пайдаланылуда."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Ашу"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Бас тарту"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Жарайды"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Параметр"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> қолданбасында <xliff:g id="TIME">%3$d</xliff:g> минут бойы <xliff:g id="TYPE">%2$s</xliff:g> пайдаланылуда"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> қолданбасында <xliff:g id="TYPE">%2$s</xliff:g> пайдаланылуда"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> қолданбасында <xliff:g id="TYPES_LIST">%2$s</xliff:g> пайдаланылуда"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Мәліметті көру"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> пайдаланып жатқан қолданба"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g> пайдаланып жатқан қолданбалар"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" және "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камера"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"геодерек"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"микрофон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Тағы <xliff:g id="NUM_APPS_1">%d</xliff:g> қолданба</item>
+ <item quantity="one">Тағы <xliff:g id="NUM_APPS_0">%d</xliff:g> қолданба</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 6c584ad..1330f36 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"តំបន់សារជំនួយ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"បញ្ជាក់"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ប៉ះឧបករណ៍ចាប់ស្នាមម្រាមដៃ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"រូបតំណាងស្នាមម្រាមដៃ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"កំពុងស្វែងរកអ្នក…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"សំឡេង"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"កាស"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"បញ្ចូល"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"កំពុងបើក..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ពន្លឺ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"បង្វិលស្វ័យប្រវត្តិ"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"ការជូនដំណឹង"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"អ្នកនឹងមិនឃើញការជូនដំណឹងទាំងនេះទៀតទេ"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"ការជូនដំណឹងទាំងនេះនឹងត្រូវបានបង្រួម"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ការជូនដំណឹងទាំងនេះនឹងត្រូវបានបង្ហាញស្ងាត់ៗ"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ការជូនដំណឹងទាំងនេះនឹងបង្ហាញដល់អ្នក"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"ជាធម្មតាអ្នកច្រានចោលការជូនដំណឹងទាំងនេះ។ \nបន្តបង្ហាញពួកវាទៀតដែរទេ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"បន្តបង្ហាញការជូនដំណឹងទាំងនេះ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"បញ្ឈប់ការជូនដំណឹង"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"បន្តបង្ហាញ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"បង្រួម"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"បង្ហាញស្ងាត់ៗ"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"បន្តបង្ហាញការជូនដំណឹងពីកម្មវិធីនេះ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"មិនអាចបិទការជូនដំណឹងទាំងនេះបានទេ"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"តាមរយៈ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"កម្មវិធីនេះកំពុងប្រើកាមេរ៉ា។"</string>
<string name="appops_microphone" msgid="741508267659494555">"កម្មវិធីនេះកំពុងប្រើមីក្រូហ្វូន។"</string>
<string name="appops_overlay" msgid="6165912637560323464">"កម្មវិធីនេះកំពុងបង្ហាញពីលើកម្មវិធីផ្សេងទៀតនៅលើអេក្រង់របស់អ្នក។"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ការកំណត់"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"យល់ហើយ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"ចម្លង SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> កំពុងប្រើ <xliff:g id="TYPES_LIST">%2$s</xliff:g> របស់អ្នក។"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"កម្មវិធីកំពុងប្រើ <xliff:g id="TYPES_LIST">%s</xliff:g> របស់អ្នក។"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"បើកកម្មវិធី"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"បោះបង់"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"យល់ព្រម"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ការកំណត់"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> កំពុងប្រើ <xliff:g id="TYPE">%2$s</xliff:g> របស់អ្នករយៈពេល <xliff:g id="TIME">%3$d</xliff:g> នាទីចុងក្រោយ"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> កំពុងប្រើ <xliff:g id="TYPE">%2$s</xliff:g> របស់អ្នក"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> កំពុងប្រើ <xliff:g id="TYPES_LIST">%2$s</xliff:g> របស់អ្នក"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"មើលព័ត៌មានលម្អិត"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"កម្មវិធីកំពុងប្រើប្រាស់ <xliff:g id="TYPES_LIST">%s</xliff:g> របស់អ្នក"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"កម្មវិធីកំពុងប្រើប្រាស់ <xliff:g id="TYPES_LIST">%s</xliff:g> របស់អ្នក"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" និង "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"កាមេរ៉ា"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ទីតាំង"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"មីក្រូហ្វូន"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">កម្មវិធី <xliff:g id="NUM_APPS_1">%d</xliff:g> ទៀត</item>
+ <item quantity="one">កម្មវិធី <xliff:g id="NUM_APPS_0">%d</xliff:g> ទៀត</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 9269026..58d2fb8 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ಸಹಾಯ ಸಂದೇಶ ಪ್ರದೇಶ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ದೃಢೀಕರಿಸಿ"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ನಿಮಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ಆಡಿಯೋ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ಹೆಡ್ಸೆಟ್"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ಇನ್ಪುಟ್"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ಪ್ರಕಾಶಮಾನ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ನೀವು ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಇನ್ನು ಮುಂದೆ ನೋಡುವುದಿಲ್ಲ"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಿರಿದುಗೊಳಿಸಲಾಗುತ್ತದೆ"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ಈ ಸೂಚನೆಗಳನ್ನು ಮೌನವಾಗಿ ತೋರಿಸಲಾಗುತ್ತದೆ"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ಈ ಸೂಚನೆಗಳು ನಿಮ್ಮನ್ನು ಎಚ್ಚರಿಸುತ್ತವೆ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"ನೀವು ಸಾಮಾನ್ಯವಾಗಿ ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ವಜಾಗೊಳಿಸಿದ್ದೀರಿ. \nಅವುಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿಲ್ಲಿಸಿ"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"ತೋರಿಸುತ್ತಲಿರಿ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ಕಿರಿದುಗೊಳಿಸಿ"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"ಮೌನವಾಗಿ ತೋರಿಸಿ"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಮೂಲಕ"</string>
<string name="appops_camera" msgid="8100147441602585776">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸುತ್ತಿದೆ."</string>
<string name="appops_microphone" msgid="741508267659494555">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ಬಳಸುತ್ತಿದೆ."</string>
<string name="appops_overlay" msgid="6165912637560323464">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ಇತರ ಅಪ್ಲಿಕೇಶನ್ಗಳ ಮೇಲಿಂದ ಪ್ರದರ್ಶಿಸುತ್ತಿದೆ."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ಅರ್ಥವಾಯಿತು"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ಹೀಪ್ ಡಂಪ್ ಮಾಡಿ"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ಅನ್ನು <xliff:g id="APP">%1$s</xliff:g> ಬಳಸುತ್ತಿದೆ."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%s</xliff:g> ಅನ್ನು ಆ್ಯಪ್ಗಳು ಬಳಸುತ್ತಿವೆ."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ಆ್ಯಪ್ ತೆರೆ"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ರದ್ದುಮಾಡಿ"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ಸರಿ"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"ಕೊನೆಯ <xliff:g id="TIME">%3$d</xliff:g> ನಿಮಿಷಕ್ಕಾಗಿ ನಿಮ್ಮ <xliff:g id="TYPE">%2$s</xliff:g> ಅನ್ನು <xliff:g id="APP">%1$s</xliff:g> ಬಳಸುತ್ತಿದೆ"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"ನಿಮ್ಮ <xliff:g id="TYPE">%2$s</xliff:g> ಅನ್ನು <xliff:g id="APPS">%1$s</xliff:g> ಬಳಸುತ್ತಿದೆ"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ಅನ್ನು <xliff:g id="APP">%1$s</xliff:g> ಬಳಸುತ್ತಿದೆ"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"ವಿವರಗಳನ್ನು ನೋಡಿ"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ಆ್ಯಪ್ ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%s</xliff:g> ಅನ್ನು ಬಳಸುತ್ತಿದೆ"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ಆ್ಯಪ್ಗಳು ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%s</xliff:g> ಅನ್ನು ಬಳಸುತ್ತಿವೆ"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ಮತ್ತು "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ಕ್ಯಾಮರಾ"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ಸ್ಥಳ"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"ಮೈಕ್ರೋಫೋನ್"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ಇತರ ಆ್ಯಪ್ಗಳು</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ಇತರ ಆ್ಯಪ್ಗಳು</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b6dda32..76fcaf5 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -28,14 +28,14 @@
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> 남았습니다."</string>
<string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> 남음, 내 사용량을 기준으로 약 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> 남음, 약 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
- <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> 남았습니다. 배터리 세이버를 사용 중입니다."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> 남았습니다. 절전 모드를 사용 중입니다."</string>
<string name="invalid_charger" msgid="2741987096648693172">"USB로 충전할 수 없습니다. 기기와 함께 제공된 충전기를 사용하세요."</string>
<string name="invalid_charger_title" msgid="2836102177577255404">"USB로 충전할 수 없습니다."</string>
<string name="invalid_charger_text" msgid="6480624964117840005">"기기와 함께 제공된 충전기를 사용하세요."</string>
<string name="battery_low_why" msgid="4553600287639198111">"설정"</string>
- <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"배터리 세이버를 사용 설정하시겠습니까?"</string>
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"절전 모드를 사용 설정하시겠습니까?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"사용"</string>
- <string name="battery_saver_start_action" msgid="8187820911065797519">"배터리 세이버 사용 설정"</string>
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"절전 모드 사용 설정"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"설정"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"자동 화면 회전"</string>
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"취소"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"도움말 메시지 영역"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"확인"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"지문 센서를 터치하세요."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"지문 아이콘"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"찾는 중..."</string>
@@ -214,9 +216,9 @@
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"비행기 모드를 사용합니다."</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"모두 음소거"</string>
<string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"알람만"</string>
- <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"알림 일시중지"</string>
- <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"알림 일시중지가 사용 중지되었습니다."</string>
- <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"알림 일시중지를 사용합니다."</string>
+ <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"방해 금지 모드"</string>
+ <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"방해 금지 모드가 사용 중지되었습니다."</string>
+ <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"방해 금지 모드를 사용합니다."</string>
<string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"블루투스"</string>
<string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"블루투스: 사용 안함"</string>
<string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"블루투스: 사용"</string>
@@ -278,7 +280,7 @@
<string name="start_dreams" msgid="5640361424498338327">"화면 보호기"</string>
<string name="ethernet_label" msgid="7967563676324087464">"이더넷"</string>
<string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"추가 옵션을 보려면 아이콘을 길게 터치하세요."</string>
- <string name="quick_settings_dnd_label" msgid="8735855737575028208">"알림 일시중지"</string>
+ <string name="quick_settings_dnd_label" msgid="8735855737575028208">"방해 금지 모드"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"중요 알림만"</string>
<string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"알람만"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"모두 차단"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"오디오"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"헤드셋"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"입력"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"켜는 중..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"밝기"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"자동 회전"</string>
@@ -424,14 +428,14 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"사용자를 삭제할까요?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"이 사용자의 모든 앱과 데이터가 삭제됩니다."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"삭제"</string>
- <string name="battery_saver_notification_title" msgid="8614079794522291840">"배터리 세이버 사용 중"</string>
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"절전 모드 사용 중"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"성능 및 백그라운드 데이터를 줄입니다."</string>
- <string name="battery_saver_notification_action_text" msgid="132118784269455533">"배터리 세이버 사용 중지"</string>
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"절전 모드 사용 중지"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 화면에 표시된 모든 것을 캡처하기 시작합니다."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"다시 표시 안함"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"모두 지우기"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"관리"</string>
- <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"알림 일시중지로 일시중지된 알림"</string>
+ <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"방해 금지 모드로 일시중지된 알림"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"시작하기"</string>
<string name="empty_shade_text" msgid="708135716272867002">"알림 없음"</string>
<string name="profile_owned_footer" msgid="8021888108553696069">"프로필이 모니터링될 수 있음"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"알림"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"더 이상 다음의 알림을 받지 않습니다"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"다음 알림이 최소화됩니다."</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"알림이 소리 없이 표시됩니다."</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"알림이 전송됩니다."</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"보통 이 알림을 닫았습니다. \n알림을 계속 표시하시겠습니까?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"이 알림을 계속 표시하시겠습니까?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"알림 중지"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"계속 표시하기"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"최소화"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"소리 없이 표시"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"이 앱의 알림을 계속 표시하시겠습니까?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"이 알림은 끌 수 없습니다"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"제공: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"앱이 카메라를 사용 중입니다."</string>
<string name="appops_microphone" msgid="741508267659494555">"앱이 마이크를 사용 중입니다."</string>
<string name="appops_overlay" msgid="6165912637560323464">"앱이 화면의 다른 앱 위에 표시되고 있습니다."</string>
@@ -644,8 +652,8 @@
<item quantity="one">%d분</item>
</plurals>
<string name="battery_panel_title" msgid="7944156115535366613">"배터리 사용량"</string>
- <string name="battery_detail_charging_summary" msgid="1279095653533044008">"충전하는 동안 배터리 세이버는 사용할 수 없습니다."</string>
- <string name="battery_detail_switch_title" msgid="6285872470260795421">"배터리 세이버"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"충전하는 동안 절전 모드는 사용할 수 없습니다."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"절전 모드"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"성능 및 백그라운드 데이터를 줄입니다."</string>
<string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> 버튼"</string>
<string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
@@ -690,9 +698,9 @@
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"캘린더"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"볼륨 컨트롤과 함께 표시"</string>
- <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"알림 일시중지"</string>
+ <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"방해 금지 모드"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"볼륨 버튼 단축키"</string>
- <string name="volume_up_silent" msgid="7141255269783588286">"볼륨 크게 시 알림 일시중지 종료"</string>
+ <string name="volume_up_silent" msgid="7141255269783588286">"볼륨 높일 시 방해 금지 모드 종료"</string>
<string name="battery" msgid="7498329822413202973">"배터리"</string>
<string name="clock" msgid="7416090374234785905">"시계"</string>
<string name="headset" msgid="4534219457597457353">"헤드셋"</string>
@@ -832,10 +840,10 @@
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g>, <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi가 사용 중지됨"</string>
<string name="bt_is_off" msgid="2640685272289706392">"블루투스가 사용 중지됨"</string>
- <string name="dnd_is_off" msgid="6167780215212497572">"알림 일시중지가 사용 중지됨"</string>
- <string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"알림 일시중지가 자동 규칙(<xliff:g id="ID_1">%s</xliff:g>)에 의해 사용 설정되었습니다."</string>
- <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"알림 일시중지가 앱(<xliff:g id="ID_1">%s</xliff:g>)에 의해 사용 설정되었습니다."</string>
- <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"알림 일시중지가 자동 규칙 또는 앱에 의해 사용 설정되었습니다."</string>
+ <string name="dnd_is_off" msgid="6167780215212497572">"방해 금지 모드가 사용 중지됨"</string>
+ <string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"방해 금지 모드가 자동 규칙(<xliff:g id="ID_1">%s</xliff:g>)에 의해 사용 설정되었습니다."</string>
+ <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"방해 금지 모드가 앱(<xliff:g id="ID_1">%s</xliff:g>)에 의해 사용 설정되었습니다."</string>
+ <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"방해 금지 모드가 자동 규칙 또는 앱에 의해 사용 설정되었습니다."</string>
<string name="qs_dnd_until" msgid="3469471136280079874">"<xliff:g id="ID_1">%s</xliff:g>까지"</string>
<string name="qs_dnd_keep" msgid="1825009164681928736">"유지"</string>
<string name="qs_dnd_replace" msgid="8019520786644276623">"바꾸기"</string>
@@ -851,24 +859,29 @@
<string name="slice_permission_checkbox" msgid="7986504458640562900">"<xliff:g id="APP">%1$s</xliff:g>에서 모든 앱의 슬라이스를 표시하도록 허용"</string>
<string name="slice_permission_allow" msgid="2340244901366722709">"허용"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"거부"</string>
- <string name="auto_saver_title" msgid="1217959994732964228">"탭하여 배터리 세이버 예약"</string>
+ <string name="auto_saver_title" msgid="1217959994732964228">"탭하여 절전 모드 예약"</string>
<string name="auto_saver_text" msgid="6324376061044218113">"배터리가 <xliff:g id="PERCENTAGE">%d</xliff:g>%%가 되면 자동으로 켜기"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"사용 안함"</string>
- <string name="auto_saver_enabled_title" msgid="6726474226058316862">"배터리 세이버 예약 사용 설정됨"</string>
- <string name="auto_saver_enabled_text" msgid="874711029884777579">"배터리가 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 아래로 내려가면 배터리 세이버가 자동으로 켜집니다."</string>
+ <string name="auto_saver_enabled_title" msgid="6726474226058316862">"절전 모드 예약 사용 설정됨"</string>
+ <string name="auto_saver_enabled_text" msgid="874711029884777579">"배터리가 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 아래로 내려가면 절전 모드가 자동으로 켜집니다."</string>
<string name="open_saver_setting_action" msgid="8314624730997322529">"설정"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"확인"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g>이(가) <xliff:g id="TYPES_LIST">%2$s</xliff:g>을(를) 사용 중입니다."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"애플리케이션이 <xliff:g id="TYPES_LIST">%s</xliff:g>을(를) 사용 중입니다."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"앱 열기"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"취소"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"확인"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"설정"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g>이(가) 지난 <xliff:g id="TIME">%3$d</xliff:g>분 동안 <xliff:g id="TYPE">%2$s</xliff:g>을(를) 사용 중입니다."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g>이(가) <xliff:g id="TYPE">%2$s</xliff:g>을(를) 사용 중입니다."</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g>이(가) <xliff:g id="TYPES_LIST">%2$s</xliff:g>을(를) 사용 중입니다."</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"세부정보 보기"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g>을(를) 사용 중인 앱"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g>을(를) 사용 중인 앱"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" 및 "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"카메라"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"위치"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"마이크"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">기타 앱 <xliff:g id="NUM_APPS_1">%d</xliff:g>개</item>
+ <item quantity="one">기타 앱 <xliff:g id="NUM_APPS_0">%d</xliff:g>개</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index f4dbcbd..54f8da3 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Жардам билдирүүсү"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Ырастоо"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Манжа изинин сенсорун басыңыз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Манжа изинин сүрөтчөсү"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Жүзүңүз изделүүдө…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Гарнитура"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Киргизүү"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Күйгүзүлүүдө…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Жарыктыгы"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматтык бурулуу"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Билдирмелер"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Мындан ары бул эскертмелер сизге көрсөтүлбөйт"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Бул эскертмелер кичирейтилет"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Бул билдирмелер үнсүз көрсөтүлөт"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Бул билдирмелер үн менен эскертилет"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Адатта мындай эскертмелерди өткөрүп жибересиз. \nАлар көрсөтүлө берсинби?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Бул эскертмелер көрсөтүлө берсинби?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Эскертмелерди токтотуу"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Көрсөтүлө берсин"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Кичирейтүү"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Үнсүз көрсөтүү"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Бул колдонмонун эскертмелери көрсөтүлө берсинби?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Бул эскертмелерди өчүрүүгө болбойт"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> аркылуу"</string>
<string name="appops_camera" msgid="8100147441602585776">"Бул колдонмо камераны колдонууда."</string>
<string name="appops_microphone" msgid="741508267659494555">"Бул колдонмо микрофонду колдонууда."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Бул колдонмо экрандагы башка терезелердин үстүнөн көрсөтүлүүдө."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Жөндөөлөр"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Түшүндүм"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> төмөнкүлөрдү колдонуп жатат: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Колдонмолор төмөнкүлөрдү пайдаланып жатышат: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Колдн ачуу"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Жок"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Макул"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Жөндөөлөр"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> төмөнкүлөрдү <xliff:g id="TIME">%3$d</xliff:g> мүн. бери колдонуп жатат: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> төмөнкүлөрдү колдонуп жатышат: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> төмөнкүлөрдү колдонуп жатат: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Кеңири маалымат"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> программаларын пайдаланып жаткан колдонмо"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"<xliff:g id="TYPES_LIST">%s</xliff:g> программаларын пайдаланып жаткан колдонмолор"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" жана "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камера"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"жайгашкан жер"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"микрофон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Дагы <xliff:g id="NUM_APPS_1">%d</xliff:g> колдонмо</item>
+ <item quantity="one">Дагы <xliff:g id="NUM_APPS_0">%d</xliff:g> колдонмо</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 2a508b3..8cc18e9 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ຊ່ວຍພື້ນທີ່ຂໍ້ຄວາມ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ຢືນຢັນ"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ໄອຄອນລາຍນິ້ວມື"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ກຳລັງຊອກຫາທ່ານ…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ສຽງ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ຊຸດຫູຟັງ"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ການປ້ອນຂໍ້ມູນ"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ກຳລັງເປີດ..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ຄວາມສະຫວ່າງ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ໝຸນອັດຕະໂນມັດ"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"ໂດຍປົກກະຕິທ່ານປິດການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄວ້. \nສືບຕໍ່ສະແດງພວກມັນບໍ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ສະແດງການແຈ້ງເຕືອນເຫຼົ່ານີ້ຕໍ່ໄປບໍ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ຢຸດການແຈ້ງເຕືອນ"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"ສະແດງຕໍ່ໄປ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ຫຍໍ້"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"ສະແດງແບບງຽບໆ"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"ສະແດງ ແລະ ເຕືອນ"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ສະແດງການແຈ້ງເຕືອນຈາກແອັບນີ້ຕໍ່ໄປບໍ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ບໍ່ສາມາດປິດການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄດ້"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"ຜ່ານ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"ແອັບນີ້ກຳລັງໃຊ້ກ້ອງຢູ່."</string>
<string name="appops_microphone" msgid="741508267659494555">"ແອັບນີ້ກຳລັງໃຊ້ໄມໂຄຣໂຟນຢູ່."</string>
<string name="appops_overlay" msgid="6165912637560323464">"ແອັບນີ້ກຳລັງສະແດງຜົນບັງແອັບອື່ນຢູ່ໜ້າຈໍຂອງທ່ານ."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ການຕັ້ງຄ່າ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ຂອງທ່ານ."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ແອັບພລິເຄຊັນກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%s</xliff:g> ຂອງທ່ານ."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ເປີດແອັບ"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ຍົກເລີກ"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ຕົກລົງ"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ການຕັ້ງຄ່າ"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງໃຊ້ <xliff:g id="TYPE">%2$s</xliff:g> ຂອງທ່ານ <xliff:g id="TIME">%3$d</xliff:g> ນທ ທີ່ຜ່ານມາ"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ກຳລັງໃຊ້ <xliff:g id="TYPE">%2$s</xliff:g> ຂອງທ່ານ"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ຂອງທ່ານ"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"ເບິ່ງລາຍລະອຽດ"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ແອັບກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%s</xliff:g> ຂອງທ່ານ"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ແອັບກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%s</xliff:g> ຂອງທ່ານ"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ແລະ "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ກ້ອງຖ່າຍຮູບ"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ສະຖານທີ່"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"ໄມໂຄຣໂຟນ"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ແອັບອື່ນໆ</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ແອັບອື່ນໆ</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 0792bbb..e8d6438 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Pagalbos pranešimo sritis"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Patvirtinkite"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Palieskite piršto antspaudo jutiklį"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Piršto antspaudo piktograma"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ieškoma jūsų…"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Garsas"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Virtualiosios realybės įrenginys"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Įvestis"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Įjungiama…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Šviesumas"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatinis pasukimas"</string>
@@ -604,21 +608,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Pranešimai"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nebematysite šių pranešimų"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Šie pranešimai bus sumažinti"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Šie pranešimai bus rodomi tyliai"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Šie pranešimai įspės jus"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Paprastai šių pranešimų atsisakote. \nToliau juos rodyti?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Toliau rodyti šiuos pranešimus?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Sustabdyti pranešimus"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Toliau rodyti"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Sumažinti"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Rodyti tyliai"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Toliau rodyti iš šios programos gautus pranešimus?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Šių pranešimų negalima išjungti"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"naudojant „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ši programa naudoja fotoaparatą."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ši programa naudoja mikrofoną."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ši programa rodoma ekrane virš kitų programų."</string>
@@ -869,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Nustatymai"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Supratau"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Pat. „SysUI“ krūvą"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Programa „<xliff:g id="APP">%1$s</xliff:g>“ naudoja: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Programos naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Atid. pr."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Atšaukti"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Gerai"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Nustatymai"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Programa „<xliff:g id="APP">%1$s</xliff:g>“ pastar. <xliff:g id="TIME">%3$d</xliff:g> min. naudoja: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Programos (<xliff:g id="APPS">%1$s</xliff:g>) naudoja: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Programa „<xliff:g id="APP">%1$s</xliff:g>“ naudoja: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Žr. išsam. inf."</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Programa, kuri naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Programos, kurios naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ir "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"fotoaparatą"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"vietovę"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofoną"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">Dar <xliff:g id="NUM_APPS_1">%d</xliff:g> programa</item>
+ <item quantity="few">Dar <xliff:g id="NUM_APPS_1">%d</xliff:g> programos</item>
+ <item quantity="many">Dar <xliff:g id="NUM_APPS_1">%d</xliff:g> programos</item>
+ <item quantity="other">Dar <xliff:g id="NUM_APPS_1">%d</xliff:g> programų</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 14a859c..4c6282d 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Palīdzības ziņojuma apgabals"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Apstiprināt"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Pieskarieties pirksta nospieduma sensoram"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Pirksta nospieduma ikona"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Notiek jūsu sejas meklēšana…"</string>
@@ -291,6 +293,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Austiņas"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Ievade"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Notiek ieslēgšana…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Spilgtums"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automātiska pagriešana"</string>
@@ -601,21 +605,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Paziņojumi"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Jūs vairs neredzēsiet šos paziņojumus."</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Šie paziņojumi tiks minimizēti"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Šie paziņojumi tiks rādīti bez skaņas signāla"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Šie paziņojumi tiks rādīti, lai jūs brīdinātu"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Parasti jūs noraidāt šādus paziņojumus. \nVai turpināt tos rādīt?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Vai turpināt rādīt šos paziņojumus?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Apturēt paziņojumu rādīšanu"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Turpināt rādīt"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizēt"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Rādīt bez skaņas signāla"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vai turpināt rādīt paziņojumus no šīs lietotnes?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Šos paziņojumus nevar izslēgt."</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Šajā lietotnē tiek izmantota kamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Šajā lietotnē tiek izmantots mikrofons."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Šī lietotne tiek rādīta ekrānā pāri citām lietotnēm."</string>
@@ -864,16 +872,22 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Iestatījumi"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Labi"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Lietotne <xliff:g id="APP">%1$s</xliff:g> izmanto funkcijas <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Lietojumprogrammas izmanto šādas funkcijas: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Atvērt lietotni"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Atcelt"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Labi"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Iestatījumi"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Lietotne <xliff:g id="APP">%1$s</xliff:g> pēdējās <xliff:g id="TIME">%3$d</xliff:g> min izmanto funkciju <xliff:g id="TYPE">%2$s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Lietotnes <xliff:g id="APPS">%1$s</xliff:g> izmanto funkciju <xliff:g id="TYPE">%2$s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Lietotne <xliff:g id="APP">%1$s</xliff:g> izmanto funkcijas <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Skatīt detalizētu informāciju"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Lietotne, kurā tiek izmantots: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Lietotnes, kurās tiek izmantots: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" un "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"atrašanās vieta"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofons"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="zero">Vēl <xliff:g id="NUM_APPS_1">%d</xliff:g> lietotņu</item>
+ <item quantity="one">Vēl <xliff:g id="NUM_APPS_1">%d</xliff:g> lietotne</item>
+ <item quantity="other">Vēl <xliff:g id="NUM_APPS_1">%d</xliff:g> lietotnes</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index cc937e9..8591932 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област за пораки за помош"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Допрете го сензорот за отпечатоци"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатоци"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ве бараме вас…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Слушалки"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Влез"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Се вклучува…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Осветленост"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматско ротирање"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Известувања"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Веќе нема да ги гледате овие известувања"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Овие известувања ќе се минимизираат"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Овие известувања ќе се прикажуваат тивко"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Овие известувања ќе ве предупредуваат"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Обично ги отфрлате известувањава. \nДа продолжат да се прикажуваат?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Дали да продолжат да се прикажуваат известувањава?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Запри ги известувањата"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Продолжи да ги прикажуваш"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Минимизирај"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Прикажи тивко"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Дали да продолжат да се прикажуваат известувања од апликацијава?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Известувањава не може да се исклучат"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"преку <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Апликацијава ја користи камерата."</string>
<string name="appops_microphone" msgid="741508267659494555">"Апликацијава го користи микрофонот."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Апликацијава се прикажува врз други апликации на вашиот екран."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Поставки"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Сфатив"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Извади SysUI-слика"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> користи <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Апликациите користат <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Отвори апликација"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Откажи"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Во ред"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Поставки"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> користи <xliff:g id="TYPE">%2$s</xliff:g> во последните <xliff:g id="TIME">%3$d</xliff:g> мин."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> користат <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> користи <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Прикажи ги деталите"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Апликации што ја користат вашата <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Апликации што ја користат вашата <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" и "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камера"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"локација"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"микрофон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> друга апликација</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> други апликации</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index b88fbd6..51ebb2e 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"സഹായ സന്ദേശ ഏരിയ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"സ്ഥിരീകരിക്കുക"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"വിരലടയാള സെൻസർ സ്പർശിക്കുക"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"വിരലടയാള ഐക്കൺ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"നിങ്ങൾക്കായി തിരയുന്നു…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ഓഡിയോ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ഹെഡ്സെറ്റ്"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ഇൻപുട്ട്"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ഓണാക്കുന്നു…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"തെളിച്ചം"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"സ്ക്രീൻ സ്വമേധയാ തിരിയുക"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"അറിയിപ്പുകൾ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"നിങ്ങൾ ഇനി ഈ അറിയിപ്പുകൾ കാണില്ല"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"ഈ അറിയിപ്പുകൾ ചെറുതാക്കപ്പെടും"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ഈ അറിയിപ്പുകൾ നിശബ്ദമായി കാണിക്കും"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ഈ അറിയിപ്പുകൾ നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകും"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"സാധാരണയായി നിങ്ങൾ ഈ അറിയിപ്പുകൾ നിരാകരിക്കുന്നു. \nഅവ തുടർന്നും കാണിക്കണോ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ഈ അറിയിപ്പുകൾ തുടർന്നും കാണിക്കണോ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"അറിയിപ്പുകൾ നിർത്തുക"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"തുടർന്നും കാണിക്കുക"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ചെറുതാക്കുക"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"നിശബ്ദമായി കാണിക്കുക"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ഈ ആപ്പിൽ നിന്നുള്ള അറിയിപ്പുകൾ തുടർന്നും കാണിക്കണോ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ഈ അറിയിപ്പുകൾ ഓഫാക്കാനാവില്ല"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> വഴി"</string>
<string name="appops_camera" msgid="8100147441602585776">"ഈ ആപ്പ് ക്യാമറ ഉപയോഗിക്കുന്നുണ്ട്."</string>
<string name="appops_microphone" msgid="741508267659494555">"ഈ ആപ്പ് മൈക്രോഫോൺ ഉപയോഗിക്കുന്നു."</string>
<string name="appops_overlay" msgid="6165912637560323464">"ഈ ആപ്പ് നിങ്ങളുടെ സ്ക്രീനിലെ മറ്റ് ആപ്പുകൾക്ക് മുകളിൽ പ്രദർശിപ്പിക്കുന്നു."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ക്രമീകരണം"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"മനസ്സിലായി"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ഹീപ്പ് ഡമ്പ് ചെയ്യുക"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ഉപയോഗിക്കുന്നു."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ആപ്പുകൾ നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്നു."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ആപ്പ് തുറക്കുക"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"റദ്ദാക്കുക"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ശരി"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ക്രമീകരണം"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"കഴിഞ്ഞ <xliff:g id="TIME">%3$d</xliff:g> മിനിറ്റായി <xliff:g id="APP">%1$s</xliff:g> നിങ്ങളുടെ <xliff:g id="TYPE">%2$s</xliff:g> ഉപയോഗിക്കുന്നു"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> നിങ്ങളുടെ <xliff:g id="TYPE">%2$s</xliff:g> ഉപയോഗിക്കുന്നു"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g>നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ഉപയോഗിക്കുന്നു"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"വിശദാംശങ്ങൾ കാണുക"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്ന ആപ്പ്"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്ന ആപ്പുകൾ"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" കൂടാതെ "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ക്യാമറ"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ലൊക്കേഷന്"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"മൈക്രോഫോൺ"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">മറ്റ് <xliff:g id="NUM_APPS_1">%d</xliff:g> ആപ്പുകൾ</item>
+ <item quantity="one">മറ്റ് <xliff:g id="NUM_APPS_0">%d</xliff:g> ആപ്പ്</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 7033766..6be0e71 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Тусламжийн зурвасын хэсэг"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Баталгаажуулах"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Хурууны хээ мэдрэгчид хүрэх"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Хурууны хээний дүрс тэмдэг"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Таныг хайж байна…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Чихэвч"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Оролт"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Асааж байна…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Тодрол"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматаар эргэх"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Мэдэгдэл"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Та эдгээр мэдэгдлийг цаашид харахгүй"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Энэ мэдэгдлийг багасгана"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Эдгээр мэдэгдлийг дуугүй харуулна"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Эдгээр мэдэгдлийг танд мэдэгдэнэ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Та эдгээр мэдэгдлийг ихэвчлэн хаадаг. \nЭдгээрийг харуулсан хэвээр байх уу?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Эдгээр мэдэгдлийг харуулсан хэвээр байх уу?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Мэдэгдлийг зогсоох"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Харуулсан хэвээр байх"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Багасгах"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Дуугүй харуулах"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Энэ аппаас мэдэгдэл харуулсан хэвээр байх уу?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Эдгээр мэдэгдлийг унтраах боломжгүй"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>-р"</string>
<string name="appops_camera" msgid="8100147441602585776">"Энэ апп камерыг ашиглаж байна."</string>
<string name="appops_microphone" msgid="741508267659494555">"Энэ апп микрофоныг ашиглаж байна."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Энэ аппыг таны дэлгэцэд бусад аппын дээр харуулж байна."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Тохиргоо"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Ойлголоо"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> таны <xliff:g id="TYPES_LIST">%2$s</xliff:g>-г ашиглаж байна."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Аппууд таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Аппыг нээх"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Болих"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"За"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Тохиргоо"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> таны <xliff:g id="TYPE">%2$s</xliff:g>-г сүүлийн <xliff:g id="TIME">%3$d</xliff:g> минутын турш ашиглаж байна"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> таны <xliff:g id="TYPE">%2$s</xliff:g>-г ашиглаж байна"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> таны <xliff:g id="TYPES_LIST">%2$s</xliff:g>-г ашиглаж байна"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Дэлгэрэнгүй үзэх"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Апп таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Аппууд таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" болон "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камер"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"байршил"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"микрофон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Бусад <xliff:g id="NUM_APPS_1">%d</xliff:g> апп</item>
+ <item quantity="one">Бусад <xliff:g id="NUM_APPS_0">%d</xliff:g> апп</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 159926a..6fb44cc 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -89,7 +89,7 @@
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाईल स्थानांतरण पर्याय"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"कॅमेरा म्हणून माउंट करा (PTP)"</string>
- <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac साठी Android फाईल स्थानांतर अॅप इंस्टॉल करा"</string>
+ <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac साठी Android फाईल स्थानांतर अॅप इंस्टॉल करा"</string>
<string name="accessibility_back" msgid="567011538994429120">"मागे"</string>
<string name="accessibility_home" msgid="8217216074895377641">"होम"</string>
<string name="accessibility_menu" msgid="316839303324695949">"मेनू"</string>
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"मदत मेसेज परिसर"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"खात्री करा"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिंट सेन्सरला स्पर्श करा"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिंट आयकन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तुमच्यासाठी शोधत आहे…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ऑडिओ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"हेडसेट"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"इनपुट"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"सुरू करत आहे…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"चमक"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ऑटो-रोटेट"</string>
@@ -594,32 +598,36 @@
<string name="tuner_full_importance_settings" msgid="3207312268609236827">"पॉवर सूचना नियंत्रणे"</string>
<string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"चालू"</string>
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
- <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
+ <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"आता तुम्हाला या सूचना दिसणार नाहीत"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"या सूचना लहान केल्या जातील"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"या सूचना शांतपणे दर्शविल्या जातील"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"या सूचना तुम्हाला इशारा देतील"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"तुम्ही या सूचना सामान्यतः डिसमिस करता. \nते दाखवत राहायचे?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"या सूचना दाखवणे सुरू ठेवायचे?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"सूचना थांबवा"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"दाखवणे सुरू ठेवा"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"लहान करा"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"शांतपणे दर्शवा"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"या अॅपकडील सूचना दाखवणे सुरू ठेवायचे?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"या सूचना बंद करता येत नाहीत"</string>
- <string name="appops_camera" msgid="8100147441602585776">"हे अॅप कॅमेरा वापरत आहे."</string>
- <string name="appops_microphone" msgid="741508267659494555">"हे अॅप मायक्रोफोन वापरत आहे."</string>
- <string name="appops_overlay" msgid="6165912637560323464">"हे अॅप स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे."</string>
- <string name="appops_camera_mic" msgid="1576901651150187433">"हे अॅप मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
- <string name="appops_camera_overlay" msgid="8869400080809298814">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि कॅमेरा वापरत आहे."</string>
- <string name="appops_mic_overlay" msgid="4835157962857919804">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन वापरत आहे."</string>
- <string name="appops_camera_mic_overlay" msgid="6718768197048030993">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> मार्गे"</string>
+ <string name="appops_camera" msgid="8100147441602585776">"हे अॅप कॅमेरा वापरत आहे."</string>
+ <string name="appops_microphone" msgid="741508267659494555">"हे अॅप मायक्रोफोन वापरत आहे."</string>
+ <string name="appops_overlay" msgid="6165912637560323464">"हे अॅप स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे."</string>
+ <string name="appops_camera_mic" msgid="1576901651150187433">"हे अॅप मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_camera_overlay" msgid="8869400080809298814">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_mic_overlay" msgid="4835157962857919804">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन वापरत आहे."</string>
+ <string name="appops_camera_mic_overlay" msgid="6718768197048030993">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
<string name="notification_appops_settings" msgid="1028328314935908050">"सेटिंग्ज"</string>
<string name="notification_appops_ok" msgid="1156966426011011434">"ओके"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string>
@@ -768,8 +776,8 @@
<string name="accessibility_qs_edit_tile_move" msgid="3108103090006972938">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर हलवा"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"द्रुत सेटिंग्ज संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
- <string name="dock_forced_resizable" msgid="5914261505436217520">"अॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string>
- <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"अॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
+ <string name="dock_forced_resizable" msgid="5914261505436217520">"अॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"अॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
<string name="forced_resizable_secondary_display" msgid="4230857851756391925">"दुसऱ्या डिस्प्लेवर अॅप कदाचित चालणार नाही."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="7793821742158306742">"दुसऱ्या डिस्प्लेवर अॅप लाँच होणार नाही."</string>
<string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"सेटिंग्ज उघडा."</string>
@@ -798,7 +806,7 @@
<string name="pip_skip_to_prev" msgid="1955311326688637914">"डावलून मागे जा"</string>
<string name="thermal_shutdown_title" msgid="4458304833443861111">"तापल्यामुळे फोन बंद झाला"</string>
<string name="thermal_shutdown_message" msgid="9006456746902370523">"तुमचा फोन आता व्यवस्थित चालू आहे"</string>
- <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n •मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
+ <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n •मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
<string name="high_temp_title" msgid="4589508026407318374">"फोन ऊष्ण होत आहे"</string>
<string name="high_temp_notif_message" msgid="5642466103153429279">"फोन थंड होत असताना काही वैशिष्ट्ये मर्यादित असतात"</string>
<string name="high_temp_dialog_message" msgid="6840700639374113553">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
@@ -824,9 +832,9 @@
<string name="notification_channel_hints" msgid="7323870212489152689">"सूचना"</string>
<string name="instant_apps" msgid="6647570248119804907">"इन्सटंट अॅप्स"</string>
<string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> रन होत आहे"</string>
- <string name="instant_apps_message" msgid="1183313016396018086">"इंस्टॉल केल्याशिवाय अॅप उघडले."</string>
- <string name="instant_apps_message_with_help" msgid="6179830437630729747">"इंस्टॉल केल्याशिवाय अॅप उघडले. अधिक जाणून घेण्यासाठी टॅप करा."</string>
- <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"इंस्टॉल केल्याशिवाय अॅप उघडले."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"इंस्टॉल केल्याशिवाय अॅप उघडले. अधिक जाणून घेण्यासाठी टॅप करा."</string>
+ <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
<string name="go_to_web" msgid="2650669128861626071">"ब्राउझरवर जा"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"सेटिंग्ज"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"समजले"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI हीप डंप करा"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> तुमचे <xliff:g id="TYPES_LIST">%2$s</xliff:g> वापरत आहे."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"अॅप्लिकेशन्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"अॅप उघडा"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"रद्द करा"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ओके"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"सेटिंग्ज"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"शेवटच्या <xliff:g id="TIME">%3$d</xliff:g> मिनिटासाठी <xliff:g id="APP">%1$s</xliff:g> तुमचे <xliff:g id="TYPE">%2$s</xliff:g> वापरत आहे"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> तुमचे <xliff:g id="TYPE">%2$s</xliff:g> वापरत आहे"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> तुमचे <xliff:g id="TYPES_LIST">%2$s</xliff:g> वापरत आहे"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"तपशील पाहा"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"अॅप तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"अॅप्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहेत"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" आणि "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"कॅमेरा"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"स्थान"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"मायक्रोफोन"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅप</item>
+ <item quantity="other">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅप्स</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index dbaccac..58a417d 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Bahagian mesej bantuan"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Sahkan"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sentuh penderia cap jari"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon cap jari"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari anda…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Set Kepala"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Menghidupkan…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Kecerahan"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Autoputar"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Pemberitahuan"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Anda tidak akan melihat pemberitahuan ini lagi"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Pemberitahuan ini akan diminimumkan"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Pemberitahuan ini akan ditunjukkan secara senyap"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Pemberitahuan ini akan memaklumi anda"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Biasanya anda mengetepikan pemberitahuan ini. \nTerus tunjukkan pemberitahuan?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Terus tunjukkan pemberitahuan ini?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Hentikan pemberitahuan"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Terus tunjukkan"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimumkan"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Tunjukkan secara senyap"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tunjukkan pemberitahuan daripada apl ini?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Pemberitahuan ini tidak boleh dimatikan"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"melalui <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Apl ini sedang menggunakan kamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Apl ini sedang menggunakan mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Apl ini dipaparkan di atas apl lain pada skrin anda."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Tetapan"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Longgok Tmbunn SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g> anda."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Buka apl"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Batal"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Okey"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Tetapan"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPE">%2$s</xliff:g> anda selama <xliff:g id="TIME">%3$d</xliff:g> min yang lalu"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPE">%2$s</xliff:g> anda"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g> anda"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Lihat butiran"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Apl yang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apl yang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" dan "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokasi"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> apl lain</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g>apl lain</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 2a1bf77..aa52f7b 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"အကူအညီမက်ဆေ့ဂျ် နေရာ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"အတည်ပြုပါ"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"လက်ဗွေအာရုံခံကိရိယာကို တို့ပါ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"လက်ဗွေ သင်္ကေတ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"သင့်ကို ရှာဖွေနေသည်…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"အသံ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"မိုက်ခွက်ပါနားကြပ်"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"အဝင်"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ဖွင့်နေသည်…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"အလင်းတောက်ပမှု"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"အော်တို-လည်"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"သင်သည် အများအားဖြင့် ဤအကြောင်းကြားချက်များကို ပယ်လေ့ရှိပါသည်။ \n၎င်းတို့ကို ဆက်လက်ပြသလိုပါသလား။"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ဤအကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"အကြောင်းကြားချက်များကို ရပ်ရန်"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"ဆက်ပြရန်"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ချုံ့ရန်"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"တိတ်တဆိတ် ပြရန်"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"ပြပြီး သတိပေးရန်"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ဤအက်ပ်ထံမှ အကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ဤအကြောင်းကြားချက်များကို ပိတ်၍မရပါ"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> မှတစ်ဆင့်"</string>
<string name="appops_camera" msgid="8100147441602585776">"ဤအက်ပ်က ကင်မရာကို အသုံးပြုနေသည်။"</string>
<string name="appops_microphone" msgid="741508267659494555">"ဤအက်ပ်က မိုက်ခရိုဖုန်းကို အသုံးပြုနေသည်။"</string>
<string name="appops_overlay" msgid="6165912637560323464">"ဤအက်ပ်က ဖန်သားမျက်နှာပြင်ပေါ်ရှိ အခြားအက်ပ်များ အပေါ်မှ ထပ်ပြီး ပြသနေပါသည်။"</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ဆက်တင်များ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ရပါပြီ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> က သင်၏ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ကို အသုံးပြုနေသည်။"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"အပလီကေးရှင်းများက သင်၏ <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသည်။"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ဖွင့်ရန်"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"မလုပ်တော့"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Okay"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ဆက်တင်များ"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> သည် ပြီးခဲ့သော <xliff:g id="TIME">%3$d</xliff:g> မိနစ်က သင်၏ <xliff:g id="TYPE">%2$s</xliff:g> ကို အသုံးပြုနေသည်"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> တို့က သင်၏ <xliff:g id="TYPE">%2$s</xliff:g> ကို အသုံးပြုနေသည်"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> က သင်၏ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ကို အသုံးပြုနေသည်"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"အသေးစိတ်ကြည့်ပါ"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"သင့် <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသော အက်ပ်"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"သင့် <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသော အက်ပ်များ"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"၊ "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" နှင့် "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ကင်မရာ"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"တည်နေရာ"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"မိုက်ခရိုဖုန်း"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">အခြားအက်ပ် <xliff:g id="NUM_APPS_1">%d</xliff:g> ခု</item>
+ <item quantity="one">အခြားအက်ပ် <xliff:g id="NUM_APPS_0">%d</xliff:g> ခု</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 4014957..558fd9b 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Område for hjelpemelding"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekreft"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Trykk på fingeravtrykkssensoren"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon for fingeravtrykk"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ser etter deg …"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Hodetelefoner"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Innenhet"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Slår på …"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Lysstyrke"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotér automatisk"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Varsler"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Du ser ikke disse varslene lenger"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Disse varslene minimeres"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Disse varslene vises lydløst"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Disse varslene varsler deg"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Du avviser vanligvis disse varslene. \nVil du fortsette å vise dem?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Vil du fortsette å vise disse varslene?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stopp varsler"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Fortsett å vise"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimer"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Vis uten lyd"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsette å vise varsler fra denne appen?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Du kan ikke slå av disse varslene"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Denne appen bruker kameraet."</string>
<string name="appops_microphone" msgid="741508267659494555">"Denne appen bruker mikrofonen."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Denne appen vises over andre apper på skjermen."</string>
@@ -777,7 +785,7 @@
<string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Lukk hurtiginnstillingene."</string>
<string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Alarm er angitt."</string>
<string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Logget på som <xliff:g id="ID_1">%s</xliff:g>"</string>
- <string name="data_connection_no_internet" msgid="4503302451650972989">"Ingen Internett-tilkobling"</string>
+ <string name="data_connection_no_internet" msgid="4503302451650972989">"Ingen internettilkobling"</string>
<string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Åpne informasjonen."</string>
<string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Utilgjengelig fordi <xliff:g id="REASON">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Åpne <xliff:g id="ID_1">%s</xliff:g>-innstillingene."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Innstillinger"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Greit"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI-heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> bruker <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Apper bruker <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Åpne appen"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Avbryt"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Ok"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Innst."</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> har brukt <xliff:g id="TYPE">%2$s</xliff:g> de siste <xliff:g id="TIME">%3$d</xliff:g> minuttene"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> bruker <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> bruker <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Se detaljer"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App som bruker <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apper som bruker <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" og "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"posisjon"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andre apper</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> annen app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 67e4223..b826420 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"मद्दतसम्बन्धी सन्देशको क्षेत्र"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि गर्नुहोस्"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिन्ट सेन्सरमा छुनुहोस्"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिन्ट जनाउने आइकन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तपाईंलाई खोज्दै…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"अडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"हेडसेट"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"इनपुट"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"सक्रिय गर्दै…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"चमक"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"स्वतःघुम्ने"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचनाहरू"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"तपाईं अब उप्रान्त यी सूचनाहरू देख्नु हुने छैन"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"यी सूचनाहरू सानो बनाइने छ"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"यी सूचनाहरू मौन रूपमा देखाइने छ"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"यी सूचनाहरूले तपाईंलाई सतर्क गरिने छ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"तपाईं सामान्यतया यी सूचनाहरूलाई खारेज गर्ने गर्नुहुन्छ। \nतिनलाई देखाइरहने हो?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"यी सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"सूचनाहरू देखाउन छाड्नुहोस्"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"देखाउने क्रम जारी राख्नुहोस्"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"सानो बनाउनुहोस्"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"मौन रूपमा देखाउने"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"यो अनुप्रयोगका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"यी सूचनाहरूलाई निष्क्रिय पार्न सकिँदैन"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> मार्फत"</string>
<string name="appops_camera" msgid="8100147441602585776">"यो अनुप्रयोगले क्यामेराको प्रयोग गर्दै छ।"</string>
<string name="appops_microphone" msgid="741508267659494555">"यो अनुप्रयोगले माइक्रोफोनको प्रयोग गर्दै छ।"</string>
<string name="appops_overlay" msgid="6165912637560323464">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमाथि प्रदर्शन गर्दै छ।"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"सेटिङहरू"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"बुझेँ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ले तपाईंको <xliff:g id="TYPES_LIST">%2$s</xliff:g> प्रयोग गर्दै छ।"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"अनुप्रयोगहरूले तपाईंको <xliff:g id="TYPES_LIST">%s</xliff:g> प्रयोग गर्दै छन्।"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"अनुप्रयोग खोल्नुहोस्"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"रद्द गर्नु…"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ठिक छ"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"सेटिङहरू"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> ले विगत <xliff:g id="TIME">%3$d</xliff:g> मिनेट देखि तपाईंको <xliff:g id="TYPE">%2$s</xliff:g> प्रयोग गर्दै छन्"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ले तपाईंको <xliff:g id="TYPE">%2$s</xliff:g> प्रयोग गर्दै छन्"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> ले तपाईंको <xliff:g id="TYPES_LIST">%2$s</xliff:g> प्रयोग गर्दै छ"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"विवरणहरू हेर्नुहोस्"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"तपाईंको <xliff:g id="TYPES_LIST">%s</xliff:g> प्रयोग गरिरहेका अनुप्रयोग"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"तपाईंको <xliff:g id="TYPES_LIST">%s</xliff:g> प्रयोग गरिरहेका अनुप्रयोगहरू"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" र "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"क्यामेरा"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"स्थान"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"माइक्रोफोन"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>अन्य अनुप्रयोगहरू</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> अन्य अनुप्रयोग</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 30ccad8..06467b8 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Gebied voor Help-berichten"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bevestigen"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Raak de vingerafdruksensor aan"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Vingerafdrukpictogram"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Jouw gezicht zoeken…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Invoer"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Inschakelen..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Helderheid"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatisch draaien"</string>
@@ -596,19 +600,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Uit"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Met beheeropties voor meldingen met betrekking tot stroomverbruik kun je een belangrijkheidsniveau van 0 tot 5 instellen voor de meldingen van een app. \n\n"<b>"Niveau 5"</b>" \n- Boven aan de lijst met meldingen weergeven \n- Onderbreking op volledig scherm toestaan \n- Altijd korte weergave \n\n"<b>"Niveau 4"</b>" \n- Geen onderbreking op volledig scherm \n- Altijd korte weergave \n\n"<b>"Niveau 3"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n\n"<b>"Niveau 2"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n\n"<b>"Niveau 1"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n- Verbergen op vergrendelingsscherm en statusbalk \n- Onder aan de lijst met meldingen weergeven \n\n"<b>"Niveau 0"</b>" \n- Alle meldingen van de app blokkeren"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Meldingen"</string>
- <string name="notification_channel_disabled" msgid="344536703863700565">"Deze meldingen worden niet meer weergegeven"</string>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Meldingen worden niet meer weergegeven"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Deze meldingen worden geminimaliseerd"</string>
<string name="notification_channel_silenced" msgid="2877199534497961942">"Deze meldingen worden zonder geluid weergegeven"</string>
<string name="notification_channel_unsilenced" msgid="4790904571552394137">"Deze meldingen stellen je op de hoogte"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Meestal sluit je deze meldingen. \nWil je ze blijven weergeven?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Deze meldingen blijven weergeven?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Meldingen stoppen"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Blijven weergeven"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimaliseren"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Zonder geluid weergeven"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Weergeven en melden"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Meldingen van deze app blijven weergeven?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Deze meldingen kunnen niet worden uitgeschakeld"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Deze app gebruikt de camera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Deze app gebruikt de microfoon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Deze app wordt over andere apps op je scherm heen weergegeven."</string>
@@ -639,7 +651,7 @@
<item quantity="other">%d minuten</item>
<item quantity="one">%d minuut</item>
</plurals>
- <string name="battery_panel_title" msgid="7944156115535366613">"Accugebruik"</string>
+ <string name="battery_panel_title" msgid="7944156115535366613">"Batterijgebruik"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterijbesparing niet beschikbaar tijdens opladen"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterijbesparing"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vermindert de prestaties en achtergrondgegevens"</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Instellingen"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> gebruikt je <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Apps gebruiken je <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"App openen"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Annuleren"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Instellingen"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> gebruikt je <xliff:g id="TYPE">%2$s</xliff:g> gedurende de afgelopen <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> gebruiken je <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> gebruikt je <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Details weergeven"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App die je <xliff:g id="TYPES_LIST">%s</xliff:g> gebruikt"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps die je <xliff:g id="TYPES_LIST">%s</xliff:g> gebruiken"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" en "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"camera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"locatie"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microfoon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andere apps</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> andere app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1768c69a..de7f3b2 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ସାହାଯ୍ୟ ମେସେଜ୍ କ୍ଷେତ୍ର"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ନିଶ୍ଚିତ କରନ୍ତୁ"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନସର୍କୁ ଛୁଅଁନ୍ତୁ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ଆପଣଙ୍କୁ ଚିହ୍ନଟ କରୁଛି…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ଅଡିଓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ହେଡସେଟ୍"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ଇନପୁଟ୍"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ଅନ୍ ହେଉଛି…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ଉଜ୍ଜ୍ୱଳତା"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ସ୍ୱତଃ-ଘୂର୍ଣ୍ଣନ"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"ବିଜ୍ଞପ୍ତି"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଆପଣ ଆଉ ଦେଖିବାକୁ ପାଇବେନାହିଁ।"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ମିନିମାଇଜ୍ ହୋଇଯିବ"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ଏହି ବିଜ୍ଞପ୍ତି ନିରବରେ ଦେଖାଯିବ"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡିକ ଆପଣଙ୍କୁ ଆଲର୍ଟ କରିବ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"ସାଧାରଣତଃ ଆପଣ ଏହି ବିଜ୍ଞପ୍ତିକୁ ଖାରଜ କରିଦିଅନ୍ତି। \n ସେଗୁଡ଼ିକୁ ଦେଖାଇବା ଜାରି ରଖିବେ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଦେଖାଇବା ଜାରି ରଖିବେ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ବିଜ୍ଞପ୍ତିକୁ ଦେଖାଇବା ବନ୍ଦ କରନ୍ତୁ"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"ଦେଖାଇବା ଜାରି ରଖନ୍ତୁ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ଛୋଟ କରନ୍ତୁ"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"ନିରବରେ ଦେଖାନ୍ତୁ"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ଏହି ଆପ୍ରୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଦେଖାଇବା ଜାରି ରଖିବେ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ବନ୍ଦ କରିହେବ ନାହିଁ"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ମାଧ୍ୟମରେ"</string>
<string name="appops_camera" msgid="8100147441602585776">"ଏହି ଆପ୍ କ୍ୟାମେରା ବ୍ୟବହାର କରୁଛି।"</string>
<string name="appops_microphone" msgid="741508267659494555">"ଏହି ଆପ୍, ମାଇକ୍ରୋଫୋନ୍ ବ୍ୟବହାର କରୁଛି।"</string>
<string name="appops_overlay" msgid="6165912637560323464">"ଏହି ଆପ୍, ଆପଣଙ୍କର ସ୍କ୍ରୀନ୍ ଉପରେ ଥିବା ଅନ୍ୟ ଆପ୍ ଉପରେ ପ୍ରଦର୍ଶିତ ହେଉଛି।"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ସେଟିଙ୍ଗ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ବୁଝିଲି"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ହିପ୍ ଡମ୍ପ୍ କରନ୍ତୁ"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି।"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ଆପ୍ଲିକେସନ୍ଗୁଡିକ ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି।"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ଆପ୍ ଖୋଲନ୍ତୁ"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ବାତିଲ୍"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ଠିକ୍ ଅଛି"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ସେଟିଂସ୍"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> ବିଗତ <xliff:g id="TIME">%3$d</xliff:g> ମିନିଟ୍ ହେବ ଆପଣଙ୍କ <xliff:g id="TYPE">%2$s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି।"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="TYPE">%2$s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"ବିବରଣୀ ଦେଖନ୍ତୁ"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ଆପ୍ ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%s</xliff:g> ବ୍ୟବହାର କରୁଛି"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ଆପ୍ସ ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ଏବଂ "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"କ୍ୟାମେରା"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ଲୋକେସନ୍"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"ମାଇକ୍ରୋଫୋନ୍"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>ଟି ଅନ୍ୟ ଆପ୍</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g>ଟି ଅନ୍ୟ ଆପ୍</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index dbcd43c..7883bbd 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"ਰੱਦ ਕਰੋ"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ਮਦਦ ਸੁਨੇਹਾ ਖੇਤਰ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ਪੁਸ਼ਟੀ ਕਰੋ"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਪ੍ਰਤੀਕ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ਤੁਹਾਡੀ ਪਛਾਣ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ਆਡੀਓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ਹੈੱਡਸੈੱਟ"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ਇਨਪੁੱਟ"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ਚਮਕ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ਆਟੋ-ਰੋਟੇਟ"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"ਸੂਚਨਾਵਾਂ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ਤੁਹਾਨੂੰ ਹੁਣ ਇਹ ਸੂਚਨਾਵਾਂ ਦਿਖਾਈ ਨਹੀਂ ਦੇਣਗੀਆਂ"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਛੋਟਾ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ਇਹ ਸੂਚਨਾਵਾਂ ਚੁੱਪ-ਚਪੀਤੇ ਦਿਖਾਈਆਂ ਜਾਣਗੀਆਂ"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ਇਹ ਸੂਚਨਾਵਾਂ ਤੁਹਾਨੂੰ ਸੁਚੇਤ ਕਰਨਗੀਆਂ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"ਤੁਸੀਂ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਆਮ ਤੌਰ \'ਤੇ ਖਾਰਜ ਕਰਦੇ ਹੋ। \nਕੀ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ਕੀ ਇਨ੍ਹਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖੋ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ਛੋਟਾ ਕਰੋ"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"ਚੁੱਪ-ਚਪੀਤੇ ਦਿਖਾਓ"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ਕੀ ਇਸ ਐਪ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ਇਨ੍ਹਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਰਾਹੀਂ"</string>
<string name="appops_camera" msgid="8100147441602585776">"ਇਹ ਐਪ ਕੈਮਰੇ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string>
<string name="appops_microphone" msgid="741508267659494555">"ਇਹ ਐਪ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string>
<string name="appops_overlay" msgid="6165912637560323464">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਹੋਰਾਂ ਐਪਾਂ ਉੱਪਰ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹੈ।"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ਸੈਟਿੰਗਾਂ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ਸਮਝ ਲਿਆ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ਹੀਪ ਡੰਪ ਕਰੋ"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ਐਪਲੀਕੇਸ਼ਨਾਂ ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ।"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ਐਪ ਖੋਲ੍ਹੋ"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ਰੱਦ ਕਰੋ"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ਠੀਕ ਹੈ"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ਸੈਟਿੰਗਾਂ"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> ਪਿਛਲੇ <xliff:g id="TIME">%3$d</xliff:g> ਮਿੰਟ ਤੋਂ ਤੁਹਾਡੇ <xliff:g id="TYPE">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="TYPE">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"ਵੇਰਵੇ ਦੇਖੋ"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਐਪ"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਐਪਾਂ"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ਅਤੇ "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ਕੈਮਰਾ"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ਟਿਕਾਣਾ"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ਹੋਰ ਐਪ</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ਹੋਰ ਐਪਾਂ</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index f9c1c79..81c2689 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Obszar komunikatu pomocy"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potwierdź"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotknij czytnika linii papilarnych"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona odcisku palca"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Szukam Cię…"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Zestaw słuchawkowy"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Wejście"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Włączam…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Jasność"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Autoobracanie"</string>
@@ -604,21 +608,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Powiadomienia"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Te powiadomienia nie będą już wyświetlane"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Te powiadomienia zostaną zminimalizowane"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Te powiadomienia będą dyskretne"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Te powiadomienia będą Cię ostrzegać"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Zwykle odrzucasz te powiadomienia. \nNadal je pokazywać?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Nadal pokazywać te powiadomienia?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Zablokuj powiadomienia"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Pokazuj nadal"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimalizuj"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Pokazuj dyskretnie"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Nadal pokazywać powiadomienia z tej aplikacji?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Tych powiadomień nie można wyłączyć"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ta aplikacja używa aparatu."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ta aplikacja używa mikrofonu."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ta aplikacja wyświetla się nad innymi aplikacjami na ekranie."</string>
@@ -869,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Ustawienia"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Zrzut stosu SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> używa: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikacje używają: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Otwórz"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Anuluj"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Ustawienia"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> używa: <xliff:g id="TYPE">%2$s</xliff:g> od <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Aplikacje <xliff:g id="APPS">%1$s</xliff:g> używają: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> używa: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Wyświetl szczegóły"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacje, które używają: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacje, które używają: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" i "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"aparat"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokalizacja"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="few"><xliff:g id="NUM_APPS_1">%d</xliff:g> innych aplikacji</item>
+ <item quantity="many"><xliff:g id="NUM_APPS_1">%d</xliff:g> innej aplikacji</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> inne aplikacje</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> inna aplikacja</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index cd469e5..05fcfb8 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressão digital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Fone de ouvido"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrada"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Ativando…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Girar automaticamente"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Essas notificações serão minimizadas"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Essas notificações serão mostradas silenciosamente"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Essas notificações alertarão você"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Geralmente você dispensa essas notificações. \nQuer que elas continuem a ser exibidas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostrar silenciosamente"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"pelo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Este app está usando a câmera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Este app está usando o microfone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Este app está sobreposto a outros apps na sua tela."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Configurações"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Ok"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Despejar pilha SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"O app <xliff:g id="APP">%1$s</xliff:g> está usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Abrir app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancelar"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Ok"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Config."</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"O app <xliff:g id="APP">%1$s</xliff:g> está usando <xliff:g id="TYPE">%2$s</xliff:g> há <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Os apps <xliff:g id="APPS">%1$s</xliff:g> estão usando <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"O app <xliff:g id="APP">%1$s</xliff:g> está usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ver detalhes"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" e "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"câmera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"localização"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microfone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> outro app</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> outros apps</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8006f33..fafdad0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressões digitais."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"À sua procura…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Ausc. c/ mic. integ."</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrada"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"A ativar..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotação automática"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nunca mais verá estas notificações."</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Estas notificações serão minimizadas."</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Estas notificações serão mostradas silenciosamente."</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Estas notificações irão alertá-lo."</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Normalmente, ignora estas notificações. \nPretende continuar a mostrá-las?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Pretende continuar a ver estas notificações?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Parar notificações"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar a mostrar"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostrar silenciosamente"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Pretende continuar a ver notificações desta aplicação?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar estas notificações."</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"através da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Esta aplicação está a utilizar a câmara."</string>
<string name="appops_microphone" msgid="741508267659494555">"Esta aplicação está a utilizar o microfone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Esta aplicação está a sobrepor-se a outras aplicações no ecrã."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Definições"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Compreendi"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Cp ár. di. da. SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"A aplicação <xliff:g id="APP">%1$s</xliff:g> está a utilizar o(a) <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"As aplicações estão a utilizar o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Abrir aplicação"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancelar"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Definições"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"A aplicação <xliff:g id="APP">%1$s</xliff:g> está a utilizar o(a) <xliff:g id="TYPE">%2$s</xliff:g> há <xliff:g id="TIME">%3$d</xliff:g> min."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"As aplicações <xliff:g id="APPS">%1$s</xliff:g> estão a utilizar o(a) <xliff:g id="TYPE">%2$s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"A aplicação <xliff:g id="APP">%1$s</xliff:g> está a utilizar o(a) <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ver detalhes"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicações que utilizam o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicações que utilizam o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" e "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"câmara"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"localização"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microfone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> outras aplicações</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> outra aplicação</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index cd469e5..05fcfb8 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressão digital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Fone de ouvido"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrada"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Ativando…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Girar automaticamente"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Essas notificações serão minimizadas"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Essas notificações serão mostradas silenciosamente"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Essas notificações alertarão você"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Geralmente você dispensa essas notificações. \nQuer que elas continuem a ser exibidas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Mostrar silenciosamente"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"pelo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Este app está usando a câmera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Este app está usando o microfone."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Este app está sobreposto a outros apps na sua tela."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Configurações"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Ok"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Despejar pilha SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"O app <xliff:g id="APP">%1$s</xliff:g> está usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Abrir app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Cancelar"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Ok"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Config."</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"O app <xliff:g id="APP">%1$s</xliff:g> está usando <xliff:g id="TYPE">%2$s</xliff:g> há <xliff:g id="TIME">%3$d</xliff:g> min"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Os apps <xliff:g id="APPS">%1$s</xliff:g> estão usando <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"O app <xliff:g id="APP">%1$s</xliff:g> está usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ver detalhes"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Apps usando <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" e "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"câmera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"localização"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microfone"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> outro app</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> outros apps</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 43a0825..cb9b574 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Anulați"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zona mesajelor de ajutor"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmați"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Atingeți senzorul de amprente"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Pictograma amprentă"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Vă căutăm…"</string>
@@ -291,6 +293,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Căști"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Intrare"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Se activează..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminozitate"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotire automată"</string>
@@ -601,21 +605,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificări"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nu veți mai vedea aceste notificări"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Aceste notificări vor fi minimizate"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Aceste notificări vor fi afișate fără sunet"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Aceste notificări vă vor anunța"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"De regulă respingeți aceste notificări. \nDoriți să fie afișate în continuare?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Doriți să continuați afișarea acestor notificări?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Opriți notificările"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Continuați afișarea"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizați"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Afișați fără sunet"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Doriți să continuați afișarea notificărilor de la această aplicație?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Aceste notificări nu pot fi dezactivate"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"prin <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Această aplicație folosește camera foto."</string>
<string name="appops_microphone" msgid="741508267659494555">"Această aplicație folosește microfonul."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Această aplicație se afișează pe alte aplicații de pe ecran."</string>
@@ -864,16 +872,22 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Setări"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Date SysUI memorie"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> folosește <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplicațiile folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Deschideți aplicația"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Anulați"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Setări"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> folosește <xliff:g id="TYPE">%2$s</xliff:g> de <xliff:g id="TIME">%3$d</xliff:g> minute"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> folosesc <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> folosește <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Afișați detaliile"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplicație care folosește <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplicații care folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" și "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"cameră foto"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"locație"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"microfon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="few">Alte <xliff:g id="NUM_APPS_1">%d</xliff:g> aplicații</item>
+ <item quantity="other">Alte <xliff:g id="NUM_APPS_1">%d</xliff:g> de aplicații</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> altă aplicație</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index fb74332..f9de77c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Справочное сообщение"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Подтвердить"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Прикоснитесь к сканеру отпечатков пальцев."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок отпечатка пальца"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Поиск лица…"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Аудиоустройство"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Гарнитура"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Устройство ввода"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Включение…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Яркость"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоповорот"</string>
@@ -604,21 +608,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Уведомления"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Эти уведомления отключены."</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Эти уведомления будут свернуты."</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Эти уведомления будут приходить без звука"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Мы будем сообщать вам об этих уведомлениях"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Обычно вы скрываете эти уведомления.\nПоказывать их?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Показывать эти уведомления?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Отключить уведомления"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Показывать"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Свернуть"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Без звука"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Показывать уведомления от этого приложения?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Эти уведомления нельзя отключить."</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"через приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="appops_camera" msgid="8100147441602585776">"Это приложение использует камеру."</string>
<string name="appops_microphone" msgid="741508267659494555">"Это приложение использует микрофон."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Это приложение располагается поверх других приложений."</string>
@@ -869,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Открыть настройки"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ОК"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Передача SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"В приложении \"<xliff:g id="APP">%1$s</xliff:g>\" используется <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"В приложениях используется <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Открыть"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Отмена"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ОК"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Настройки"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"В приложении \"<xliff:g id="APP">%1$s</xliff:g>\" уже <xliff:g id="TIME">%3$d</xliff:g> мин. используется <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"В нескольких приложениях (<xliff:g id="APPS">%1$s</xliff:g>) используется <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"В приложении \"<xliff:g id="APP">%1$s</xliff:g>\" используется <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Подробнее"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Приложение, в котором используются операции <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Приложения, в которых используются операции <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" и "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камера"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"местоположение"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"микрофон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">Ещё <xliff:g id="NUM_APPS_1">%d</xliff:g> приложение</item>
+ <item quantity="few">Ещё <xliff:g id="NUM_APPS_1">%d</xliff:g> приложения</item>
+ <item quantity="many">Ещё <xliff:g id="NUM_APPS_1">%d</xliff:g> приложений</item>
+ <item quantity="other">Ещё <xliff:g id="NUM_APPS_1">%d</xliff:g> другого приложения</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 3efd747..cd24df6 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"උදවු පණිවිඩ ප්රදේශය"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"තහවුරු කරන්න"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ඇඟිලි සලකුණු නිරූපකය"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ඔබව සොයමින්…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ශ්රව්ය"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"හෙඩ්සෙටය"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ආදානය"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ක්රියාත්මක කරමින්…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"දීප්තිය"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ස්වයංක්රීය කරකැවීම"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"දැනුම් දීම්"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ඔබට තවදුරටත් මෙම දැනුම්දීම් නොදකිනු ඇත"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"මෙම දැනුම්දීම් කුඩා කරනු ලැබේ"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"මෙම දැනුම්දීම් නිහඬව පෙන්වනු ඇත"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"මෙම දැනුම්දීම් ඔබට අනතුරු අඟවනු ඇත"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"ඔබ සාමාන්යයෙන් මෙවැනි දැනුම්දීම් ඉවත දමයි. \nඒවා දිගටම පෙන්වන්නද?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"මෙම දැනුම්දීම් පෙන්වමින් තබන්නද?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"දැනුම්දීම් නවත්වන්න"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"පෙන්වමින් තබන්න"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"කුඩා කරන්න"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"නිහඬව පෙන්වන්න"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"මෙම යෙදුම වෙතින් දැනුම්දීම් පෙන්වමින් තබන්නද?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"මෙම දැනුම්දීම් ක්රියාවිරහිත කළ නොහැකිය"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> හරහා"</string>
<string name="appops_camera" msgid="8100147441602585776">"මෙම යෙදුම කැමරාව භාවිතා කරයි."</string>
<string name="appops_microphone" msgid="741508267659494555">"මෙම යෙදුම මයික්රෆෝනය භාවිතා කරයි."</string>
<string name="appops_overlay" msgid="6165912637560323464">"මෙම යෙදුම් ඔබගේ තිරය මත අනෙකුත් යෙදුම්වලට උඩින් සංදර්ශනය වේ."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"සැකසීම්"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"තේරුණා"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ඔබේ <xliff:g id="TYPES_LIST">%2$s</xliff:g> භාවිත කරමින් සිටී."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"යෙදුම් ඔබේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරමින් සිටී."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"යෙදුම විවෘත කරන්න"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"අවලංගු කරන්න"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"හරි"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"සැකසීම්"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> අවසන් මිනි <xliff:g id="TIME">%3$d</xliff:g> සඳහා ඔබේ <xliff:g id="TYPE">%2$s</xliff:g> භාවිත කරමින් සිටී"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ඔබේ <xliff:g id="TYPE">%2$s</xliff:g> භාවිත කරමින් සිටී"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> ඔබේ <xliff:g id="TYPES_LIST">%2$s</xliff:g> භාවිත කරමින් සිටී"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"විස්තර බලන්න"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ඔබගේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරන යෙදුම්"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ඔබගේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරන යෙදුම්"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" සහ "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"කැමරාව"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ස්ථානය"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"මයික්රෝෆෝනය"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">වෙනත් යෙදුම් <xliff:g id="NUM_APPS_1">%d</xliff:g></item>
+ <item quantity="other">වෙනත් යෙදුම් <xliff:g id="NUM_APPS_1">%d</xliff:g></item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index e11acd6..941e9c7 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Oblasť správy pomocníka"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdiť"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Klepnite na senzor odtlačkov prstov"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona odtlačku prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hľadáme vás…"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Náhlavná súprava"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Vstup"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Zapína sa…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Jas"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatické otáčanie"</string>
@@ -609,12 +613,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Tieto upozornenia zvyčajne odmietate. \nChcete ich naďalej zobrazovať?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Majú sa tieto upozornenia naďalej zobrazovať?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Prestať zobrazovať upozornenia"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Naďalej zobrazovať"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimalizovať"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Zobraziť potichu"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Zobraziť a upozorniť"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Majú sa upozornenia z tejto aplikácie naďalej zobrazovať?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Tieto upozornenia sa nedajú vypnúť"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"prostredníctvom aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Táto aplikácia používa fotoaparát."</string>
<string name="appops_microphone" msgid="741508267659494555">"Táto aplikácia používa mikrofón."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Táto aplikácia sa zobrazuje cez ďalšie aplikácie na obrazovke."</string>
@@ -865,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Nastavenia"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Dobre"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Výpis haldy SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> používa zoznam <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikácie používajú zoznam <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Otvoriť"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Zrušiť"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Nastavenia"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> používa typ <xliff:g id="TYPE">%2$s</xliff:g> posl. <xliff:g id="TIME">%3$d</xliff:g> min."</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Aplikácie <xliff:g id="APPS">%1$s</xliff:g> používajú typ <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> používa zoznam <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Podrobnosti"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikácia používajúca <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikácie používajúce <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" a "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"fotoaparát"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"poloha"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofón"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="few"><xliff:g id="NUM_APPS_1">%d</xliff:g> ďalšie aplikácie</item>
+ <item quantity="many"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ďalších aplikácií</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ďalšia aplikácia</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index b050497..e5555c7 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Prekliči"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Območje sporočila pomoči"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potrdite"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotaknite se tipala prstnih odtisov"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona prstnih odtisov"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Preverjanje vašega obraza …"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Zvok"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Slušalke z mikrofonom"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Vhodna naprava"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Vklapljanje …"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Svetlost"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Samodejno sukanje"</string>
@@ -604,21 +608,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Obvestila"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Ta obvestila ne bodo več prikazana"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ta obvestila bodo minimirana"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ta obvestila bodo prikazana brez zvoka"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ta obvestila vas bodo opozorila nase"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Ta obvestila običajno opustite. \nAli želite, da se še naprej prikazujejo?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Želite, da so ta obvestila še naprej prikazana?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Ustavi prikazovanje obvestil"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Prikazuj še naprej"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimiraj"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Prikaži brez zvoka"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite, da so obvestila te aplikacije še naprej prikazana?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Teh obvestil ni mogoče izklopiti"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"prek aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ta aplikacija uporablja fotoaparat."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ta aplikacija uporablja mikrofon."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ta aplikacija prekriva druge aplikacije na zaslonu."</string>
@@ -869,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Nastavitve"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"V redu"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Izvoz kopice SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> uporablja <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikacije uporabljajo <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Odpri apl."</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Prekliči"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"V redu"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Nastavitve"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> uporablja <xliff:g id="TYPE">%2$s</xliff:g> zadnjih toliko minut: <xliff:g id="TIME">%3$d</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Aplikacije <xliff:g id="APPS">%1$s</xliff:g> uporabljajo <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> uporablja <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Podrobnosti"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacija, ki uporablja te funkcije: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacije, ki uporabljajo te funkcije: <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" in "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"fotoaparat"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokacijo"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">in še <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacija</item>
+ <item quantity="two">in še <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikaciji</item>
+ <item quantity="few">in še <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacije</item>
+ <item quantity="other">in še <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacij</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 146e91c..ba1f117 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Anulo"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zona e mesazhit të ndihmës"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Konfirmo"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Prek sensorin e gjurmës së gishtit"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona e gjurmës së gishtit"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Po të kërkojmë…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Kufje me mikrofon"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Hyrja"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Po aktivizohet…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ndriçimi"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rrotullim automatik"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Njoftime"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nuk do t\'i shikosh më këto njoftime"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Këto njoftime do të minimizohen"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Këto njoftime do të shfaqen në heshtje"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Këto njoftime do të të sinjalizojnë"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Këto njoftime ti zakonisht i largon. \nDëshiron të vazhdosh t\'i shfaqësh ato?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Do të vazhdosh t\'i shfaqësh këto njoftime?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Ndalo njoftimet"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Vazhdo të shfaqësh"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizo"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Shfaq në heshtje"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Do të vazhdosh t\'i shfaqësh njoftimet nga ky aplikacion?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Këto njoftime nuk mund të çaktivizohen"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"nëpërmjet <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ky aplikacion po përdor kamerën."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ky aplikacion po përdor mikrofonin."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ky aplikacion po shfaqet mbi aplikacionet e tjera në ekran."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Cilësimet"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"E kuptova"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Hidh grumbullin SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> po përdor <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Aplikacionet po përdorin <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Hap aplikacionin"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Anulo"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Në rregull"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Cilësimet"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> po përdor <xliff:g id="TYPE">%2$s</xliff:g> për <xliff:g id="TIME">%3$d</xliff:g> minutat e fundit"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> po përdor <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> po përdor <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Shiko detajet"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Aplikacionet që po përdorin <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Aplikacionet që po përdorin <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" dhe "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamerën"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"vendndodhjen"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofonin"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacione të tjera</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplikacion tjetër</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index a838a0b..64dd9a4 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област поруке за помоћ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Додирните сензор за отисак прста"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона отиска прста"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Тражимо вас…"</string>
@@ -291,6 +293,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Слушалице"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Унос"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Укључује се..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Осветљеност"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Аутоматска ротација"</string>
@@ -601,21 +605,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Обавештења"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Више нећете видети ова обавештења"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ова обавештења ће се умањити"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ова обавештења ће се приказивати без звука"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ова обавештења ће вас упозоравати"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Обично одбацујете ова обавештења. \nЖелите ли да се и даље приказују?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Желите ли да се ова обавештења и даље приказују?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Престани да приказујеш обавештења"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Настави да приказујеш"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Умањи"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Прикажи без звука"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Желите ли да се обавештења из ове апликације и даље приказују?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Не можете да искључите ова обавештења"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"преко апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ова апликација користи камеру."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ова апликација користи микрофон."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ова апликација се приказује преко других апликација на екрану."</string>
@@ -864,16 +872,22 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Подешавања"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Важи"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Издвоји SysUI мем."</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> користи <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Апликације користе <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Отвори"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Откажи"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Потврди"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Подешавања"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> током неколико последњих минута (<xliff:g id="TIME">%3$d</xliff:g>) користи <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> користе <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> користи <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Прикажи детаље"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Апликација која користи дозволе <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Апликације које користе дозволе <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" и "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камеру"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"локацију"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"микрофон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">И још <xliff:g id="NUM_APPS_1">%d</xliff:g> апликација</item>
+ <item quantity="few">И још <xliff:g id="NUM_APPS_1">%d</xliff:g> апликације</item>
+ <item quantity="other">И још <xliff:g id="NUM_APPS_1">%d</xliff:g> апликација</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 7c4e88a..dd3af05 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Område för hjälpmeddelande"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekräfta"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Tryck på fingeravtryckssensorn"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon för fingeravtryck"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Håller utkik efter dig …"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Ljud"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Ingång"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Aktiverar …"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ljusstyrka"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotera automatiskt"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Aviseringar"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"De här aviseringarna visas inte längre"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Dessa aviseringar minimeras"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Dessa aviseringar visas tyst"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Dessa aviseringar visas med ljud"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Du brukar avvisa de här aviseringarna. \nVill du fortsätta att visa dem?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Vill du fortsätta visa de här aviseringarna?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stoppa aviseringar"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Fortsätt visa"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimera"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Visa utan ljud"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vill du fortsätta visa aviseringar för den här appen?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"De här aviseringarna kan inte inaktiveras"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Kameran används av appen."</string>
<string name="appops_microphone" msgid="741508267659494555">"Mikrofonen används av appen."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Appen visas över andra appar på skärmen."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Inställningar"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dumpa SysUI-heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="TYPES_LIST">%2$s</xliff:g> används av <xliff:g id="APP">%1$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"<xliff:g id="TYPES_LIST">%s</xliff:g> används av appar."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Öppna app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Avbryt"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Inställn."</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="TYPE">%2$s</xliff:g> har använts av <xliff:g id="APP">%1$s</xliff:g> under de senaste <xliff:g id="TIME">%3$d</xliff:g> minuterna"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="TYPE">%2$s</xliff:g> används av <xliff:g id="APPS">%1$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="TYPES_LIST">%2$s</xliff:g> används av <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Läs mer"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"En app använder din <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Appar använder dina <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" och "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"plats"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andra appar</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> annan app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index fbc1022..5170095 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Sehemu ya ujumbe wa usaidizi"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Thibitisha"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Gusa kitambua alama ya kidole"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Aikoni ya alama ya kidole"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Inakutafuta…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Vifaa vya sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Vifaa vya kuingiza sauti"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Inawasha..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ung\'avu"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Zungusha kiotomatiki"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Arifa"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Hutaona tena arifa hizi"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Arifa hizi zitapunguzwa"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Arifa hizi zitaonyeshwa bila sauti"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Arifa hizi zitatoa sauti"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Wewe huondoa arifa hizi. \nUngependa kuzionyesha?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Ungependa kuendelea kuonyesha arifa hizi?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Acha kuonyesha arifa"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Endelea kuonyesha"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Punguza"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Ionyeshe bila kutoa sauti"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Ungependa kuendelea kuonyesha arifa kutoka programu hii?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Huwezi kuzima arifa hizi"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"kupitia <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Programu hii inatumia kamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Programu hii inatumia maikrofoni."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Programu hii inachomoza kwenye programu zingine zilizo katika skrini yako."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Mipangilio"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Nimeelewa"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> inatumia <xliff:g id="TYPES_LIST">%2$s</xliff:g> yako."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Programu zinatumia <xliff:g id="TYPES_LIST">%s</xliff:g> yako."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Fungua programu"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Ghairi"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Sawa"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Mipangilio"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> imekuwa ikitumia <xliff:g id="TYPE">%2$s</xliff:g> yako kwa dakika <xliff:g id="TIME">%3$d</xliff:g> zilizopita"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> zinatumia <xliff:g id="TYPE">%2$s</xliff:g> yako"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> inatumia <xliff:g id="TYPES_LIST">%2$s</xliff:g> yako"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Angalia maelezo"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Programu inayotumia <xliff:g id="TYPES_LIST">%s</xliff:g> yako"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Programu zinazotumia <xliff:g id="TYPES_LIST">%s</xliff:g> yako"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" na "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"mahali"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"maikrofoni"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Programu zingine <xliff:g id="NUM_APPS_1">%d</xliff:g></item>
+ <item quantity="one">Programu nyingine <xliff:g id="NUM_APPS_0">%d</xliff:g></item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 8ba895b..9f3d7ca 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"உதவிச் செய்திக்கான பகுதி"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"உறுதிப்படுத்துக"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"கைரேகை உணர்வியைத் தொடவும்"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"கைரேகை ஐகான்"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"உங்கள் முகத்தைத் தேடுகிறது…"</string>
@@ -130,7 +132,7 @@
<string name="accessibility_phone_two_bars" msgid="8384905382804815201">"சிக்னல் இரண்டு கோட்டில் உள்ளது."</string>
<string name="accessibility_phone_three_bars" msgid="8521904843919971885">"சிக்னல் மூன்று கோட்டில் உள்ளது."</string>
<string name="accessibility_phone_signal_full" msgid="6471834868580757898">"சிக்னல் முழுமையாக உள்ளது."</string>
- <string name="accessibility_no_data" msgid="4791966295096867555">"தரவு சிக்னல் இல்லை."</string>
+ <string name="accessibility_no_data" msgid="4791966295096867555">"டேட்டா சிக்னல் இல்லை."</string>
<string name="accessibility_data_one_bar" msgid="1415625833238273628">"தரவு சிக்னல் ஒரு கோட்டில் உள்ளது."</string>
<string name="accessibility_data_two_bars" msgid="6166018492360432091">"தரவின் சிக்னல் இரண்டு கோடு வரை உள்ளது."</string>
<string name="accessibility_data_three_bars" msgid="9167670452395038520">"தரவு சிக்னல் மூன்று கோட்டில் உள்ளது."</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ஆடியோ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ஹெட்செட்"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"உள்ளீடு"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ஆன் செய்கிறது…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ஒளிர்வு"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"தானாகச் சுழற்று"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"அறிவிப்புகள்"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"இந்த அறிவிப்புகளை இனி பார்க்கமாட்டீர்கள்"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"இந்த அறிவிப்புகள் சிறிதாக்கப்படும்"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"இந்த அறிவிப்புகள் ஒலிக்காமல் காட்டப்படும்"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"இந்த அறிவிப்புகள் விழிப்பூட்டலாக அமையும்"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"வழக்கமாக, இந்த அறிவிப்புகளை நிராகரிக்கிறீர்கள். \nதொடர்ந்து இவற்றைக் காட்டலாமா?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"இந்த அறிவிப்புகளைத் தொடர்ந்து காட்டவா?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"அறிவிப்புகளை நிறுத்து"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"அறிவிப்புகளைத் தொடர்ந்து காட்டு"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"சிறிதாக்கு"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"ஒலிக்காமல் காட்டு"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"இந்தப் பயன்பாட்டின் அறிவிப்புகளைத் தொடர்ந்து காட்டவா?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"இந்த அறிவிப்புகளை ஆஃப் செய்ய முடியாது"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> மூலமாக"</string>
<string name="appops_camera" msgid="8100147441602585776">"இந்த ஆப்ஸானது கேமராவை உபயோகிக்கிறது."</string>
<string name="appops_microphone" msgid="741508267659494555">"இந்த ஆப்ஸானது, மைக்ரோஃபோனை உபயோகிக்கிறது."</string>
<string name="appops_overlay" msgid="6165912637560323464">"இந்த ஆப்ஸானது, உங்கள் திரையில் பிற ஆப்ஸின் இடைமுகத்தின் மேல் தோன்றுகிறது."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"அமைப்புகள்"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"சரி"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"உங்கள் <xliff:g id="TYPES_LIST">%2$s</xliff:g>ஐ <xliff:g id="APP">%1$s</xliff:g> ஆப்ஸ் பயன்படுத்துகிறது."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"உங்கள் <xliff:g id="TYPES_LIST">%s</xliff:g> ஆகியவற்றை ஆப்ஸ் பயன்படுத்துகின்றன."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ஆப்ஸை திற"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ரத்துசெய்"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"சரி"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"அமைப்புகள்"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"கடந்த <xliff:g id="TIME">%3$d</xliff:g> நிமிடங்களாக உங்கள் <xliff:g id="TYPE">%2$s</xliff:g>ஐ <xliff:g id="APP">%1$s</xliff:g> ஆப்ஸ் பயன்படுத்துகிறது"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"உங்கள் <xliff:g id="TYPE">%2$s</xliff:g> ஆகியவற்றை <xliff:g id="APPS">%1$s</xliff:g> ஆப்ஸ் பயன்படுத்துகின்றன"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"உங்கள் <xliff:g id="TYPES_LIST">%2$s</xliff:g>ஐ <xliff:g id="APP">%1$s</xliff:g> ஆப்ஸ் பயன்படுத்துகிறது"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"விவரங்களைக் காட்டு"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"உங்கள் <xliff:g id="TYPES_LIST">%s</xliff:g> ஆகியவற்றைப் பயன்படுத்தும் ஆப்ஸ்"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"உங்கள் <xliff:g id="TYPES_LIST">%s</xliff:g> ஆகியவற்றைப் பயன்படுத்தும் ஆப்ஸ்"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" மற்றும் "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"கேமரா"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"இருப்பிடம்"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"மைக்ரோஃபோன்"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">வேறு <xliff:g id="NUM_APPS_1">%d</xliff:g> ஆப்ஸ்</item>
+ <item quantity="one">வேறு <xliff:g id="NUM_APPS_0">%d</xliff:g> ஆப்ஸ்</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 12dd31a..3184107 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"రద్దు చేయి"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"సహాయ సందేశ ప్రాంతం"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"నిర్ధారించు"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"వేలిముద్ర సెన్సార్ను తాకండి"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"వేలిముద్ర చిహ్నం"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"మీ కోసం చూస్తోంది…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"ఆడియో"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"హెడ్సెట్"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ఇన్పుట్"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ఆన్ చేస్తోంది…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ప్రకాశం"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"స్వయంచాలకంగా తిప్పడం"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"నోటిఫికేషన్లు"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ఇకపై మీకు ఈ నోటిఫికేషన్లు కనిపించవు"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"ఈ నోటిఫికేషన్లు కుదించబడ్డాయి"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"ఈ నోటిఫికేషన్లు నిశ్శబ్దంగా చూపబడతాయి"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ఈ నోటిఫికేషన్లు మిమ్మల్ని హెచ్చరిస్తాయి"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"మీరు సాధారణంగా ఈ నోటిఫికేషన్లను విస్మరిస్తారు. \nవాటి ప్రదర్శనను కొనసాగించాలా?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ఈ నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"నోటిఫికేషన్లను ఆపివేయి"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"చూపిస్తూనే ఉండు"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"కుదించు"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"నిశ్శబ్దంగా చూపండి"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ఈ యాప్ నుండి నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ఈ నోటిఫికేషన్లను ఆఫ్ చేయలేరు"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ద్వారా"</string>
<string name="appops_camera" msgid="8100147441602585776">"ఈ యాప్ ఈ కెమెరాను ఉపయోగిస్తోంది."</string>
<string name="appops_microphone" msgid="741508267659494555">"ఈ యాప్ మైక్రోఫోన్ను ఉపయోగిస్తుంది."</string>
<string name="appops_overlay" msgid="6165912637560323464">"ఈ యాప్ మీ స్క్రీన్లోని ఇతర యాప్లపై ప్రదర్శించబడుతోంది."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"సెట్టింగ్లు"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"అర్థమైంది"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"డంప్ SysUI హీప్"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> మీ <xliff:g id="TYPES_LIST">%2$s</xliff:g>ని ఉపయోగిస్తోంది."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"అప్లికేషన్లు మీ <xliff:g id="TYPES_LIST">%s</xliff:g>ని ఉపయోగిస్తున్నాయి."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"యాప్ని తెరవండి"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"రద్దు చేయండి"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"సరే"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"సెట్టింగ్లు"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"గత <xliff:g id="TIME">%3$d</xliff:g> నిమిషాలుగా <xliff:g id="APP">%1$s</xliff:g> మీ <xliff:g id="TYPE">%2$s</xliff:g>ని ఉపయోగిస్తోంది"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> మీ <xliff:g id="TYPE">%2$s</xliff:g>ని ఉపయోగిస్తున్నాయి"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> మీ <xliff:g id="TYPES_LIST">%2$s</xliff:g>ను ఉపయోగిస్తోంది"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"వివరాలను చూడండి"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"మీ <xliff:g id="TYPES_LIST">%s</xliff:g> ఉపయోగించే యాప్"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"మీ<xliff:g id="TYPES_LIST">%s</xliff:g> ఉపయోగిస్తున్న యాప్లు"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" మరియు "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"కెమెరా"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"స్థానం"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"మైక్రోఫోన్"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ఇతర యాప్లు</item>
+ <item quantity="one">మరో <xliff:g id="NUM_APPS_0">%d</xliff:g> యాప్</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 8efeabe..bf3b131 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"พื้นที่ข้อความช่วยเหลือ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ยืนยัน"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"แตะเซ็นเซอร์ลายนิ้วมือ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ไอคอนลายนิ้วมือ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"กำลังหาใบหน้าคุณ…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"เสียง"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ชุดหูฟัง"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"อินพุต"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"กำลังเปิด..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ความสว่าง"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"หมุนอัตโนมัติ"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"การแจ้งเตือน"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"คุณจะไม่เห็นการแจ้งเตือนเหล่านี้อีก"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"การแจ้งเตือนเหล่านี้จะย่อเล็กสุด"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"การแจ้งเตือนเหล่านี้จะแสดงโดยไม่ส่งเสียง"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"การแจ้งเตือนเหล่านี้จะส่งเสียงแจ้งเตือนคุณ"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"โดยปกติแล้ว คุณจะปิดการแจ้งเตือนเหล่านี้ \nต้องการให้แสดงต่อไหม"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"แสดงการแจ้งเตือนเหล่านี้ต่อไปไหม"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ปิดการแจ้งเตือน"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"แสดงต่อไป"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ย่อเล็กสุด"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"แสดงโดยไม่ส่งเสียง"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"แสดงการแจ้งเตือนจากแอปนี้ต่อไปไหม"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ปิดการแจ้งเตือนเหล่านี้ไม่ได้"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"ผ่าน <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"แอปนี้กำลังใช้กล้อง"</string>
<string name="appops_microphone" msgid="741508267659494555">"แอปนี้กำลังใช้ไมโครโฟน"</string>
<string name="appops_overlay" msgid="6165912637560323464">"แอปนี้กำลังแสดงทับแอปอื่นๆ ในหน้าจอ"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"การตั้งค่า"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"รับทราบ"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ใช้<xliff:g id="TYPES_LIST">%2$s</xliff:g>ของคุณอยู่"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"หลายแอปพลิเคชันใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณอยู่"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"เปิดแอป"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"ยกเลิก"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ตกลง"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"การตั้งค่า"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> ใช้<xliff:g id="TYPE">%2$s</xliff:g>ของคุณอยู่ในช่วง <xliff:g id="TIME">%3$d</xliff:g> นาทีที่ผ่านมา"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ใช้<xliff:g id="TYPE">%2$s</xliff:g>ของคุณอยู่"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> ใช้<xliff:g id="TYPES_LIST">%2$s</xliff:g>ของคุณอยู่"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"ดูรายละเอียด"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"มีแอปกำลังใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณ"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"มีหลายแอปกำลังใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณ"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" และ "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"กล้องถ่ายรูป"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"ตำแหน่ง"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"ไมโครโฟน"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">แอปอื่น <xliff:g id="NUM_APPS_1">%d</xliff:g> แอป</item>
+ <item quantity="one">แอปอื่น <xliff:g id="NUM_APPS_0">%d</xliff:g> แอป</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index db6ce32f..fcfd1cd 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Lugar ng mensahe ng tulong"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Kumpirmahin"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Pindutin ang fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icon ng fingerprint"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hinahanap ka…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Headset"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Input"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Ino-on…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Awtomatikong i-rotate"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Mga Notification"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Hindi mo na makikita ang mga notification na ito"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Imi-minimize ang mga notification na ito"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ipapakita ang mga notification na ito nang tahimik"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Aalertuhan ka ng mga notification na ito"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Karaniwan mong dini-dismiss ang mga ganitong notification. \nPatuloy na ipakita ang mga ito?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Patuloy na ipakita ang mga notification na ito?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Ihinto ang mga notification"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Patuloy na ipakita"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"I-minimize"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Ipakita nang tahimik"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Patuloy na ipakita ang mga notification mula sa app na ito?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Hindi maaaring i-off ang mga notification na ito"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"sa pamamagitan ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ginagamit ng app na ito ang camera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ginagamit ng app na ito ang mikropono."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ipinapakita ang app na ito sa ibabaw ng iba pang app sa iyong screen."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Mga Setting"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Itapon SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Ginagamit ng <xliff:g id="APP">%1$s</xliff:g> ang iyong <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Ginagamit ng mga application ang iyong <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Buksan: app"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Kanselahin"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Okay"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Mga Setting"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Ginagamit ng <xliff:g id="APP">%1$s</xliff:g> ang iyong <xliff:g id="TYPE">%2$s</xliff:g> para sa huling <xliff:g id="TIME">%3$d</xliff:g> (na) minuto"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Ginagamit ng <xliff:g id="APPS">%1$s</xliff:g> ang iyong <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Ginagamit ng <xliff:g id="APP">%1$s</xliff:g> ang iyong <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Tingnan ang mga detalye"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"App na gumagamit ng iyong <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Mga app na gumagamit ng iyong <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" at "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"camera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"lokasyon"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikropono"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> (na) iba pang app</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> (na) iba pang app</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c7a8150..c836875 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yardım mesajı alanı"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Onaylayın"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Parmak izi sensörüne dokunun"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Parmak izi simgesi"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yüzünüz tanınmaya çalışılıyor…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Ses"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Mikrofonlu kulaklık"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Giriş"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Açılıyor…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Parlaklık"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Otomatik döndür"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirimler"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Bu bildirimleri artık görmeyeceksiniz"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Bu bildirimler küçültülecek"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Bu bildirimler sessiz olarak gösterilecek"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Bu bildirimler sizi sesli olarak uyaracak"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Bu bildirimleri genellikle kapatıyorsunuz. \nBildirimler gösterilmeye devam edilsin mi?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Bu bildirimler gösterilmeye devam edilsin mi?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bildirimleri durdur"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Göstermeye devam et"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Küçült"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Sessiz bir şekilde göster"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu uygulamadan gelen bildirimler gösterilmeye devam edilsin mi?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirimler kapatılamaz"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ile"</string>
<string name="appops_camera" msgid="8100147441602585776">"Bu uygulama kamerayı kullanıyor."</string>
<string name="appops_microphone" msgid="741508267659494555">"Bu uygulama mikrofonu kullanıyor."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Bu uygulama, ekranınızdaki diğer uygulamaların üzerinde görüntüleniyor."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Ayarlar"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Anladım"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI Yığın Dökümü"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> şunları kullanıyor: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Uygulamalar şunları kullanıyor: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Uygulama aç"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"İptal"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Tamam"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Ayarlar"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> en az <xliff:g id="TIME">%3$d</xliff:g> dakikadır şunları kullanıyor: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> şunları kullanıyor: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> şunları kullanıyor: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Ayrıntıları göster"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Uygulama cihazınızın <xliff:g id="TYPES_LIST">%s</xliff:g> özelliklerini kullanıyor"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Uygulamalar cihazınızın <xliff:g id="TYPES_LIST">%s</xliff:g> özelliklerini kullanıyor"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" ve "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"konum"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> diğer uygulama</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> diğer uygulama</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index dfac474..6864034 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Область довідкового повідомлення"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Підтвердити"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Торкніться сканера відбитків пальців"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок відбитка пальця"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Пошук обличчя…"</string>
@@ -292,6 +294,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Аудіопристрій"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Гарнітура"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Джерело сигналу"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Увімкнення…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Яскравість"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматичне обертання"</string>
@@ -604,21 +608,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Сповіщення"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Ви більше не бачитимете цих сповіщень"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Ці сповіщення буде згорнуто"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Ці сповіщення показуватимуться без звуку"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Ці сповіщення показуватимуться зі звуком"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Ви зазвичай закриваєте ці сповіщення. \nПоказувати їх?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Чи показувати ці сповіщення надалі?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Не показувати сповіщення"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Показувати надалі"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Згорнути"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Показувати без звуку"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Чи показувати сповіщення з цього додатка надалі?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ці сповіщення не можна вимкнути"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"через додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Цей додаток використовує камеру."</string>
<string name="appops_microphone" msgid="741508267659494555">"Цей додаток використовує мікрофон."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Цей додаток відображається поверх інших додатків на екрані."</string>
@@ -869,16 +877,23 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Налаштування"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"Додаток <xliff:g id="APP">%1$s</xliff:g> використовує <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Додатки використовують <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Відкрити додаток"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Скасувати"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Параметри"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"Додаток <xliff:g id="APP">%1$s</xliff:g> використовує <xliff:g id="TYPE">%2$s</xliff:g> протягом стількох останніх хвилин: <xliff:g id="TIME">%3$d</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Додатки <xliff:g id="APPS">%1$s</xliff:g> використовують <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"Додаток <xliff:g id="APP">%1$s</xliff:g> використовує <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Докладніше"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Додаток, яким використовується <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Додатки, якими використовується <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" і "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"камеру"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"місце"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"мікрофон"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one">Ще <xliff:g id="NUM_APPS_1">%d</xliff:g> додаток</item>
+ <item quantity="few">Ще <xliff:g id="NUM_APPS_1">%d</xliff:g> додатки</item>
+ <item quantity="many">Ще <xliff:g id="NUM_APPS_1">%d</xliff:g> додатків</item>
+ <item quantity="other">Ще <xliff:g id="NUM_APPS_1">%d</xliff:g> додатка</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 8b52391..4738bcd 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"امدادی پیغام کا علاقہ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"تصدیق کریں"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"فنگر پرنٹ سینسر پر ٹچ کریں"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"فنگر پرنٹ آئیکن"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"آپ کے لیے تلاش کیا جا رہا ہے…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"آڈیو"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"ہیڈ سیٹ"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"ان پٹ"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"آن ہو رہا ہے…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"چمکیلا پن"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"خود کار طور پر گھمائیں"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"اطلاعات"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"آپ کو یہ اطلاعات مزید دکھائی نہیں دیں گی"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"ان اطلاعات کو چھوٹا کر دیا جائے گا"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"یہ اطلاعات خاموشی سے دکھائی جائیں گی"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"یہ اطلاعات آپ کو الرٹ کریں گی"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"آپ عام طور پر ان اطلاعات کو مسترد کرتے ہیں۔ \nان کو دکھاتے رہیں؟"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"یہ اطلاعات دکھانا جاری رکھیں؟"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"اطلاعات روک دیں"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"دکھانا جاری رکھیں"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"چھوٹا کریں"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"خاموشی سے دکھائیں"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"اس ایپ کی طرف سے اطلاعات دکھانا جاری رکھیں؟"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"ان اطلاعات کو آف نہیں کیا جا سکتا"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"بذریعہ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"یہ ایپ کیمرے کا استعمال کر رہی ہے۔"</string>
<string name="appops_microphone" msgid="741508267659494555">"یہ ایپ مائیکروفون کا استعمال کر رہی ہے۔"</string>
<string name="appops_overlay" msgid="6165912637560323464">"یہ ایپ آپ کی اسکرین پر دیگر ایپس پر ڈسپلے کر رہی ہے۔"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"ترتیبات"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"سمجھ آ گئی"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> آپ کی <xliff:g id="TYPES_LIST">%2$s</xliff:g> کا استعمال کر رہی ہے۔"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"ایپلیکیشنز آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں۔"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"ایپ کھولیں"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"منسوخ کریں"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"ٹھیک ہے"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"ترتیبات"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> گزشتہ <xliff:g id="TIME">%3$d</xliff:g> منٹ سے آپ کی <xliff:g id="TYPE">%2$s</xliff:g> کا استعمال کر رہی ہے۔"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> آپ کی <xliff:g id="TYPE">%2$s</xliff:g> کا استعمال کر رہی ہیں"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> آپ کی <xliff:g id="TYPES_LIST">%2$s</xliff:g> کا استعمال کر رہی ہے"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"تفصیلات دیکھیں"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"ایپ آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"ایپس آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"، "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" اور "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"کیمرا"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"مقام"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"مائیکروفون"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> دیگر ایپس</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> دیگر ایپ</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 4c3909b..53a29e0 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Bekor qilish"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yordam xabari"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"OK"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Barmoq izi skaneriga tegining"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmoq izi belgisi"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yuzingiz tekshirilmoqda…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Garnitura"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Kirish"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Yoqilmoqda…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Yorqinlik"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Avtomatik burilish"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Odatda bunday bildirishnomalarni yopasiz. \nUlar ochiq tursinmi?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Mazkur bildirishnomalar chiqaversinmi?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Chiqmasin"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Ha"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Kichraytirish"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Ovozsiz"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Chiqarish va ogohlantirish"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu ilovadan keladigan bildirishnomalar chiqaversinmi?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirishnomalarni chiqmaydigan qilish imkonsiz"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> orqali"</string>
<string name="appops_camera" msgid="8100147441602585776">"Bu ilova kameradan foydalanmoqda."</string>
<string name="appops_microphone" msgid="741508267659494555">"Bu ilova mikrofondan foydalanmoqda."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Bu ilova ekranda boshqa ilovalar ustidan ochilgan."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Sozlamalar"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> ishlatmoqda: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Ilovalarda ishlatilmoqda: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Ilovani ochish"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Bekor qilish"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Sozlamalar"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> oxirgi <xliff:g id="TIME">%3$d</xliff:g> daqiqadan beri ishlatmoqda: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> ishlatmoqda: <xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> ishlatmoqda: <xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Tafsilotlar"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"<xliff:g id="TYPES_LIST">%s</xliff:g> ishlatayotgan ilova"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Ilovalar <xliff:g id="TYPES_LIST">%s</xliff:g> ishlatmoqda"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" va "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"kamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"joylashuv"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"mikrofon"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">Yana <xliff:g id="NUM_APPS_1">%d</xliff:g> ta ilova</item>
+ <item quantity="one">Yana <xliff:g id="NUM_APPS_0">%d</xliff:g> ta ilova</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 5fbccf0..ce3da2a 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Hủy"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Vùng thông báo trợ giúp"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Xác nhận"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Chạm vào cảm biến vân tay"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Biểu tượng vân tay"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Đang tìm kiếm bạn…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Âm thanh"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Tai nghe"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Thiết bị đầu vào"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Đang bật…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Độ sáng"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Tự động xoay"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"Thông báo"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Bạn sẽ không thấy các thông báo này nữa"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"Các thông báo này sẽ được thu nhỏ"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"Các thông báo này sẽ được hiển thị mà không phát âm báo"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"Các thông báo này sẽ phát âm báo cho bạn"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"Bạn thường bỏ qua những thông báo này. \nTiếp tục hiển thị thông báo?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Tiếp tục hiển thị các thông báo này?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Dừng thông báo"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Tiếp tục hiển thị"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Thu nhỏ"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"Hiển thị không phát âm báo"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Tiếp tục hiển thị các thông báo từ ứng dụng này?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Không thể tắt các thông báo này"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"thông qua <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Ứng dụng này đang sử dụng máy ảnh."</string>
<string name="appops_microphone" msgid="741508267659494555">"Ứng dụng này đang sử dụng micrô."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Ứng dụng này đang hiển thị chồng lên các ứng dụng khác trên màn hình."</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Cài đặt"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Trích xuất bộ nhớ SysUI"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> đang dùng <xliff:g id="TYPES_LIST">%2$s</xliff:g> của bạn."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Các ứng dụng đang dùng <xliff:g id="TYPES_LIST">%s</xliff:g> của bạn."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Mở ứng dụng"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Hủy"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"OK"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Cài đặt"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g> đang dùng <xliff:g id="TYPE">%2$s</xliff:g> của bạn trong <xliff:g id="TIME">%3$d</xliff:g> phút qua"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g> đang dùng <xliff:g id="TYPE">%2$s</xliff:g> của bạn"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g> đang dùng <xliff:g id="TYPES_LIST">%2$s</xliff:g> của bạn"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Xem chi tiết"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Ứng dụng đang sử dụng <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Các ứng dụng đang sử dụng <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" và "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"máy ảnh"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"vị trí"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"micrô"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ứng dụng khác</item>
+ <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ứng dụng khác</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8ab6905..b9abd40 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"帮助消息区域"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"确认"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"请触摸指纹传感器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指纹图标"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在查找中…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"音频"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"耳机"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"输入"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"正在开启…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"亮度"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自动旋转"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"您将不会再看到这些通知"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"系统将会最小化这些通知"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"这些通知显示时不发出提示音"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"这些通知将会提醒您"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"您通常会关闭这些通知。\n是否继续显示通知?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"要继续显示这些通知吗?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"停止通知"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"继续显示"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"显示通知但不发出提示音"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"要继续显示来自此应用的通知吗?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"无法关闭这些通知"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"通过<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"此应用正在使用摄像头。"</string>
<string name="appops_microphone" msgid="741508267659494555">"此应用正在使用麦克风。"</string>
<string name="appops_overlay" msgid="6165912637560323464">"此应用正显示在屏幕上其他应用的上层。"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"设置"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"知道了"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"转储 SysUI 堆"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g>正在使用您的<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"有多个应用正在使用您的<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"打开应用"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"取消"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"确定"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"设置"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"<xliff:g id="APP">%1$s</xliff:g>在过去 <xliff:g id="TIME">%3$d</xliff:g> 分钟内一直在使用您的<xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g>正在使用您的<xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"<xliff:g id="APP">%1$s</xliff:g>正在使用您的<xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"查看详情"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"正在使用您的<xliff:g id="TYPES_LIST">%s</xliff:g>的应用"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"正在使用您的<xliff:g id="TYPES_LIST">%s</xliff:g>的应用"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"、 "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" 和 "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"相机"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"位置信息"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"麦克风"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">另外 <xliff:g id="NUM_APPS_1">%d</xliff:g> 个应用</item>
+ <item quantity="one">另外 <xliff:g id="NUM_APPS_0">%d</xliff:g> 个应用</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 19450af..5d89c84 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"說明訊息區域"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在搜尋您的臉孔…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"耳機"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"輸入"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"正在開啟…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"亮度"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自動旋轉"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"您通常會關閉這些通知。\n要繼續顯示通知嗎?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"要繼續顯示這些通知嗎?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"停止通知"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"顯示通知但不發出音效"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"顯示並提醒"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示此應用程式的通知嗎?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"無法關閉這些通知"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"透過「<xliff:g id="APP_NAME">%1$s</xliff:g>」"</string>
<string name="appops_camera" msgid="8100147441602585776">"此應用程式目前使用相機。"</string>
<string name="appops_microphone" msgid="741508267659494555">"此應用程式目前使用麥克風。"</string>
<string name="appops_overlay" msgid="6165912637560323464">"此應用程式目前透過其他應用程式在畫面上顯示內容。"</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"設定"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"知道了"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"傾印 SysUI 記憶體快照"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"開啟應用程式"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"取消"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"確定"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"設定"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"「<xliff:g id="APP">%1$s</xliff:g>」在過去 <xliff:g id="TIME">%3$d</xliff:g> 分鐘內一直使用<xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"<xliff:g id="APPS">%1$s</xliff:g>正在使用<xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"查看詳情"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"、 "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" 和 "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"相機"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"位置"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"麥克風"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">其他 <xliff:g id="NUM_APPS_1">%d</xliff:g> 個應用程式</item>
+ <item quantity="one">其他 <xliff:g id="NUM_APPS_0">%d</xliff:g> 個應用程式</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 6adc9d9..bffa9ce 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"說明訊息區域"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在尋找你的臉孔…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"耳機"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"輸入"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"開啟中…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"亮度"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自動旋轉"</string>
@@ -598,21 +602,25 @@
<string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"你不會再看到這些通知"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"系統將最小化這些通知"</string>
- <!-- no translation found for notification_channel_silenced (2877199534497961942) -->
- <skip />
- <!-- no translation found for notification_channel_unsilenced (4790904571552394137) -->
- <skip />
+ <string name="notification_channel_silenced" msgid="2877199534497961942">"系統顯示這些通知時不會發出音效"</string>
+ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"系統顯示這些通知時會發出音效"</string>
<string name="inline_blocking_helper" msgid="3055064577771478591">"你通常會關閉這些通知。\n要繼續顯示通知嗎?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"要繼續顯示這些通知嗎?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"停止顯示通知"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <!-- no translation found for inline_silent_button_silent (4411510650503783646) -->
+ <string name="inline_silent_button_silent" msgid="4411510650503783646">"顯示通知但不發出音效"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
<skip />
- <!-- no translation found for inline_silent_button_alert (2967599358027208807) -->
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
<skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示這個應用程式的通知嗎?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"無法關閉這些通知"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"透過「<xliff:g id="APP_NAME">%1$s</xliff:g>」"</string>
<string name="appops_camera" msgid="8100147441602585776">"這個應用程式正在使用相機。"</string>
<string name="appops_microphone" msgid="741508267659494555">"這個應用程式正在使用麥克風。"</string>
<string name="appops_overlay" msgid="6165912637560323464">"這個應用程式顯示在畫面上其他應用程式的上層。"</string>
@@ -859,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"設定"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"我知道了"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"傾印 SysUI 記憶體快照"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"開啟應用程式"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"取消"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"確定"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"設定"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"「<xliff:g id="APP">%1$s</xliff:g>」過去 <xliff:g id="TIME">%3$d</xliff:g> 分鐘一直在使用<xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"「<xliff:g id="APPS">%1$s</xliff:g>」正在使用<xliff:g id="TYPE">%2$s</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"查看詳細資料"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"使用<xliff:g id="TYPES_LIST">%s</xliff:g>的應用程式"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">"、 "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" 和 "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"相機"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"位置"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"麥克風"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="other">另外 <xliff:g id="NUM_APPS_1">%d</xliff:g> 個應用程式</item>
+ <item quantity="one">另外 <xliff:g id="NUM_APPS_0">%d</xliff:g> 個應用程式</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index b96b075..f7369a8 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -112,6 +112,8 @@
<string name="cancel" msgid="6442560571259935130">"Khansela"</string>
<string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Indawo yosizo lomlayezo"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Qinisekisa"</string>
+ <!-- no translation found for biometric_dialog_try_again (1900185172633183201) -->
+ <skip />
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Thinta inzwa yesigxivizo somunwe"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Isithonjana sezigxivizo zeminwe"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Kufunwa wena…"</string>
@@ -290,6 +292,8 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Umsindo"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Ihedisethi"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Okokufaka"</string>
+ <!-- no translation found for quick_settings_bluetooth_secondary_label_hearing_aids (4930931771490695395) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Iyavula..."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ukugqama"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Ukuphenduka okuzenzakalelayo"</string>
@@ -603,12 +607,20 @@
<string name="inline_blocking_helper" msgid="3055064577771478591">"Uvamise ukucashisa lezi zaziso. \nQhubeka ulokhu uzibonisa?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Qhubeka nokubonisa lezi zaziso?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Misa izaziso"</string>
+ <!-- no translation found for inline_block_button (8735843688021655065) -->
+ <skip />
<string name="inline_keep_button" msgid="6665940297019018232">"Qhubeka nokubonisa"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Nciphisa"</string>
<string name="inline_silent_button_silent" msgid="4411510650503783646">"Bonisa ngokuthulile"</string>
- <string name="inline_silent_button_alert" msgid="2967599358027208807">"Bonisa futhi xwayisa"</string>
+ <!-- no translation found for inline_silent_button_stay_silent (6308371431217601009) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_alert (7961887853830826523) -->
+ <skip />
+ <!-- no translation found for inline_silent_button_keep_alerting (327696842264359693) -->
+ <skip />
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Qhubeka nokubonisa izaziso kusuka kulolu hlelo lokusebenza?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Lezi zaziso azikwazi ukuvalwa"</string>
+ <string name="notification_delegate_header" msgid="9167022191405284627">"nge-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="appops_camera" msgid="8100147441602585776">"Lolu hlelo lokusebenza lusebenzisa ikhamera."</string>
<string name="appops_microphone" msgid="741508267659494555">"Lolu hlelo lokusebenza lusebenzisa imakrofoni."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Lolu hlelo lokusebenza luboniswa ngaphezulu kwezinye izinhlelo zokusebenza kusikrini sakho."</string>
@@ -855,16 +867,21 @@
<string name="open_saver_setting_action" msgid="8314624730997322529">"Izilungiselelo"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"Ngiyezwa"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"I-Dump SysUI Heap"</string>
+ <!-- no translation found for ongoing_privacy_chip_multiple_apps (1406406529558080714) -->
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"I-<xliff:g id="APP">%1$s</xliff:g> isebenzisa i-<xliff:g id="TYPES_LIST">%2$s</xliff:g> yakho."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Izinhlelo zokusebenza zisebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho."</string>
- <string name="ongoing_privacy_dialog_open_app" msgid="2483886665314567948">"Vula uhlelo lokusebenza"</string>
+ <!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"Khansela"</string>
- <string name="ongoing_privacy_dialog_okay" msgid="5823914553907253532">"Kulungile"</string>
- <string name="ongoing_privacy_dialog_open_settings" msgid="6382622467527049074">"Izilungiselelo"</string>
- <string name="ongoing_privacy_dialog_app_item" msgid="486085465491760739">"I-<xliff:g id="APP">%1$s</xliff:g> isebenzisa i-<xliff:g id="TYPE">%2$s</xliff:g> yakho ngeminithi lokugcina elingu-<xliff:g id="TIME">%3$d</xliff:g>"</string>
- <string name="ongoing_privacy_dialog_apps_item" msgid="9207187236823950491">"Ama-<xliff:g id="APPS">%1$s</xliff:g> asebenzisa i-<xliff:g id="TYPE">%2$s</xliff:g> yakho"</string>
- <string name="ongoing_privacy_dialog_single_app" msgid="3884812469179810924">"I-<xliff:g id="APP">%1$s</xliff:g> isebenzisa i-<xliff:g id="TYPES_LIST">%2$s</xliff:g> yakho"</string>
+ <string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"Buka imininingwane"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"Uhlelo lokusebenza olusebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho"</string>
+ <string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"Izinhlelo zokusebenza ezisebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho"</string>
+ <string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
+ <string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" kanye "</string>
<string name="privacy_type_camera" msgid="1676604631892420333">"ikhamera"</string>
<string name="privacy_type_location" msgid="6435497989657286700">"indawo"</string>
<string name="privacy_type_microphone" msgid="4153045784928554506">"imakrofoni"</string>
+ <plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
+ <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ezinye izinhlelo zokusebenza</item>
+ <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ezinye izinhlelo zokusebenza</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml
deleted file mode 100644
index 8c760fc..0000000
--- a/packages/SystemUI/res/values/arrays_car.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- These should be overriden in an overlay. The default implementation is empty.
- There needs to be correspondence per index between these arrays, which means that if there
- isn't a longpress action associated with a shortcut item, put in an empty item to make
- sure everything lines up.
- -->
- <array name="car_facet_icons" />
- <array name="car_facet_intent_uris" />
- <array name="car_facet_longpress_intent_uris" />
- <array name="car_facet_package_filters"/>
- <array name="car_facet_category_filters"/>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b0a519c..3851190 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -23,6 +23,8 @@
<dimen name="navigation_bar_size">@*android:dimen/navigation_bar_height</dimen>
<!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. -->
<dimen name="navigation_bar_min_swipe_distance">48dp</dimen>
+ <!-- The distance from a side of device of the navigation bar to start an edge swipe -->
+ <dimen name="navigation_bar_edge_swipe_threshold">60dp</dimen>
<!-- thickness (height) of the dead zone at the top of the navigation bar,
reducing false presses on navbar buttons; approx 2mm -->
@@ -359,6 +361,7 @@
<!-- The height of the qs customize header. Should be (28dp - qs_tile_margin_top_bottom). -->
<dimen name="qs_customize_header_min_height">40dp</dimen>
<dimen name="qs_tile_margin_top">18dp</dimen>
+ <dimen name="qs_tile_background_size">40dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<!-- Maximum width of quick quick settings panel. Defaults to MATCH_PARENT-->
<dimen name="qs_quick_layout_width">-1px</dimen>
@@ -881,6 +884,7 @@
<dimen name="smart_reply_button_stroke_width">1dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
<dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
+ <dimen name="smart_action_button_icon_size">24dp</dimen>
<dimen name="smart_action_button_icon_padding">10dp</dimen>
<!-- A reasonable upper bound for the height of the smart reply button. The measuring code
@@ -990,4 +994,10 @@
<dimen name="bubble_icon_size">24dp</dimen>
<!-- Default height of the expanded view shown when the bubble is expanded -->
<dimen name="bubble_expanded_default_height">400dp</dimen>
+ <!-- Height of the triangle that points to the expanded bubble -->
+ <dimen name="bubble_pointer_height">4dp</dimen>
+ <!-- Width of the triangle that points to the expanded bubble -->
+ <dimen name="bubble_pointer_width">6dp</dimen>
+ <!-- Extra padding around the dismiss target for bubbles -->
+ <dimen name="bubble_dismiss_slop">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/xml/car_volume_items.xml b/packages/SystemUI/res/xml/car_volume_items.xml
deleted file mode 100644
index 742dfdd..0000000
--- a/packages/SystemUI/res/xml/car_volume_items.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- *
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
--->
-
-<!--
- Defines all possible items on car volume settings UI, keyed by usage.
-
- This enables the CarSettings UI to associate VolumeGroups surfaced by
- CarAudioManager.getVolumeGroupCount with renderable assets (ie: title, icon)
- for presentation.
-
- Order matters in this configuration. If one volume group contains multiple
- audio usages, the first one appears in this file would be picked to be
- presented on UI.
-
- When overriding this configuration, please consult also the
- car_volume_groups.xml, which is read by car audio service.
--->
-<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
- <item car:usage="unknown"
- car:icon="@drawable/car_ic_music"/>
- <item car:usage="media"
- car:icon="@drawable/car_ic_music"/>
- <item car:usage="voice_communication"
- car:icon="@*android:drawable/ic_audio_ring_notif"/>
- <item car:usage="voice_communication_signalling"
- car:icon="@*android:drawable/ic_audio_ring_notif"/>
- <item car:usage="alarm"
- car:icon="@*android:drawable/ic_audio_alarm"/>
- <item car:usage="notification"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_ringtone"
- car:icon="@*android:drawable/ic_audio_ring_notif"/>
- <item car:usage="notification_communication_request"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_communication_instant"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_communication_delayed"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_event"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="assistance_accessibility"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="assistance_navigation_guidance"
- car:icon="@drawable/car_ic_navigation"/>
- <item car:usage="assistance_sonification"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="game"
- car:icon="@drawable/car_ic_music"/>
- <item car:usage="assistant"
- car:icon="@drawable/car_ic_music"/>
-</carVolumeItems>
-
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
index 12699d5..18dc185 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
@@ -58,4 +58,8 @@
encoder.endStream();
return true;
}
+
+ public int getDisplayId() {
+ return mWrapped.getDisplayId();
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
index a9cf857..807edf6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
@@ -25,6 +25,8 @@
import android.view.View;
import android.view.ViewRootImpl;
+import java.util.function.Consumer;
+
/**
* Helper class to apply surface transactions in sync with RenderThread.
*/
@@ -78,6 +80,35 @@
applyParams(t.mTransaction, params, t.mTmpValues);
}
+ /**
+ * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+ * attached if necessary.
+ */
+ public static void create(final View targetView,
+ final Consumer<SyncRtSurfaceTransactionApplier> callback) {
+ if (targetView == null) {
+ // No target view, no applier
+ callback.accept(null);
+ } else if (targetView.getViewRootImpl() != null) {
+ // Already attached, we're good to go
+ callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
+ } else {
+ // Haven't been attached before we can get the view root
+ targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ targetView.removeOnAttachStateChangeListener(this);
+ callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ // Do nothing
+ }
+ });
+ }
+ }
+
private static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
t.setMatrix(params.surface, params.matrix, tmpFloat9);
t.setWindowCrop(params.surface, params.windowCrop);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index b439c6c..0ec9014 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -17,6 +17,7 @@
import com.android.systemui.shared.plugins.PluginManager;
import java.util.Objects;
+import java.util.TimeZone;
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
@@ -162,6 +163,15 @@
}
/**
+ * Notifies that the time zone has changed.
+ */
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ if (mClockPlugin != null) {
+ mClockPlugin.onTimeZoneChanged(timeZone);
+ }
+ }
+
+ /**
* When plugin changes, set all kept parameters into newer plugin.
*/
private void initPluginParams() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 6b24ad5..41afa9a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -193,7 +193,8 @@
public void onClick(View v) {
mCallback.userActivity(); // Leave the screen on a bit longer
// Do not show auxiliary subtypes in password lock screen.
- mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
+ mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
+ getContext().getDisplayId());
}
});
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index be795d2..1e9d288 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -51,6 +51,7 @@
import com.google.android.collect.Sets;
import java.util.Locale;
+import java.util.TimeZone;
public class KeyguardStatusView extends GridLayout implements
ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
@@ -85,6 +86,11 @@
}
@Override
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ updateTimeZone(timeZone);
+ }
+
+ @Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (showing) {
if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
@@ -282,6 +288,10 @@
mClockView.refresh();
}
+ private void updateTimeZone(TimeZone timeZone) {
+ mClockView.onTimeZoneChanged(timeZone);
+ }
+
private void refreshFormat() {
Patterns.update(mContext);
mClockView.setFormat12Hour(Patterns.clockView12);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b55aa5c..416441e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -98,6 +98,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
+import java.util.TimeZone;
/**
* Watches for updates that may be interesting to the keyguard, and provides
@@ -151,6 +152,7 @@
private static final int MSG_BIOMETRIC_AUTHENTICATION_CONTINUE = 336;
private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337;
private static final int MSG_TELEPHONY_CAPABLE = 338;
+ private static final int MSG_TIMEZONE_UPDATE = 339;
/** Biometric authentication state: Not listening. */
private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -260,6 +262,9 @@
case MSG_TIME_UPDATE:
handleTimeUpdate();
break;
+ case MSG_TIMEZONE_UPDATE:
+ handleTimeZoneUpdate((String) msg.obj);
+ break;
case MSG_BATTERY_UPDATE:
handleBatteryUpdate((BatteryStatus) msg.obj);
break;
@@ -964,9 +969,12 @@
if (DEBUG) Log.d(TAG, "received broadcast " + action);
if (Intent.ACTION_TIME_TICK.equals(action)
- || Intent.ACTION_TIME_CHANGED.equals(action)
- || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+ || Intent.ACTION_TIME_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
+ } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+ final Message msg = mHandler.obtainMessage(
+ MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone"));
+ mHandler.sendMessage(msg);
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
@@ -1860,6 +1868,21 @@
}
/**
+ * Handle (@line #MSG_TIMEZONE_UPDATE}
+ */
+ private void handleTimeZoneUpdate(String timeZone) {
+ if (DEBUG) Log.d(TAG, "handleTimeZoneUpdate");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTimeZoneChanged(TimeZone.getTimeZone(timeZone));
+ // Also notify callbacks about time change to remain compatible.
+ cb.onTimeChanged();
+ }
+ }
+ }
+
+ /**
* Handle {@link #MSG_BATTERY_UPDATE}
*/
private void handleBatteryUpdate(BatteryStatus status) {
@@ -1904,13 +1927,18 @@
+ slotId + ", state=" + state +")");
}
+ boolean becameAbsent = false;
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.w(TAG, "invalid subId in handleSimStateChange()");
/* Only handle No SIM(ABSENT) due to handleServiceStateChange() handle other case */
if (state == State.ABSENT) {
updateTelephonyCapable(true);
+ // Even though the subscription is not valid anymore, we need to notify that the
+ // SIM card was removed so we can update the UI.
+ becameAbsent = true;
+ } else {
+ return;
}
- return;
}
SimData data = mSimDatas.get(subId);
@@ -1925,7 +1953,7 @@
data.subId = subId;
data.slotId = slotId;
}
- if (changed && state != State.UNKNOWN) {
+ if ((changed || becameAbsent) && state != State.UNKNOWN) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index f818d05..8696bb7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -26,6 +26,8 @@
import com.android.internal.telephony.IccCardConstants;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import java.util.TimeZone;
+
/**
* Callback for general information relevant to lock screen.
*/
@@ -49,6 +51,13 @@
public void onTimeChanged() { }
/**
+ * Called when time zone changes.
+ *
+ * @note When time zone changes, onTimeChanged will be called too.
+ */
+ public void onTimeZoneChanged(TimeZone timeZone) { }
+
+ /**
* Called when the carrier PLMN or SPN changes.
*/
public void onRefreshCarrierInfo() { }
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 053ea67..38dadd4 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -19,6 +19,8 @@
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.animation.ArgbEvaluator;
@@ -88,6 +90,9 @@
private int mShowPercentMode = MODE_DEFAULT;
private boolean mForceShowPercent;
private boolean mShowPercentAvailable;
+ // Some places may need to show the battery conditionally, and not obey the tuner
+ private boolean mIgnoreTunerUpdates;
+ private boolean mIsSubscribedForTunerUpdates;
private int mDarkModeBackgroundColor;
private int mDarkModeFillColor;
@@ -163,6 +168,7 @@
setClipChildren(false);
setClipToPadding(false);
+ Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);
}
public void setForceShowPercent(boolean show) {
@@ -183,6 +189,44 @@
}
/**
+ * Set {@code true} to turn off BatteryMeterView's subscribing to the tuner for updates, and
+ * thus avoid it controlling its own visibility
+ *
+ * @param ignore whether to ignore the tuner or not
+ */
+ public void setIgnoreTunerUpdates(boolean ignore) {
+ mIgnoreTunerUpdates = ignore;
+ updateTunerSubscription();
+ }
+
+ private void updateTunerSubscription() {
+ if (mIgnoreTunerUpdates) {
+ unsubscribeFromTunerUpdates();
+ } else {
+ subscribeForTunerUpdates();
+ }
+ }
+
+ private void subscribeForTunerUpdates() {
+ if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) {
+ return;
+ }
+
+ Dependency.get(TunerService.class)
+ .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ mIsSubscribedForTunerUpdates = true;
+ }
+
+ private void unsubscribeFromTunerUpdates() {
+ if (!mIsSubscribedForTunerUpdates) {
+ return;
+ }
+
+ Dependency.get(TunerService.class).removeTunable(this);
+ mIsSubscribedForTunerUpdates = false;
+ }
+
+ /**
* Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll
* revert back to dark-mode-based/tinted colors.
*
@@ -247,9 +291,7 @@
getContext().getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
updateShowPercent();
- Dependency.get(TunerService.class)
- .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
- Dependency.get(ConfigurationController.class).addCallback(this);
+ subscribeForTunerUpdates();
mUserTracker.startTracking();
}
@@ -259,8 +301,7 @@
mUserTracker.stopTracking();
mBatteryController.removeCallback(this);
getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
- Dependency.get(TunerService.class).removeTunable(this);
- Dependency.get(ConfigurationController.class).removeCallback(this);
+ unsubscribeFromTunerUpdates();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 417d516..867c917 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.volume.VolumeDialogComponent;
import java.util.function.Consumer;
@@ -132,6 +133,10 @@
return new QSTileHost(context, statusBar, iconController);
}
+ public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+ return new VolumeDialogComponent(systemUi, context);
+ }
+
public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
Context context) {
providers.put(StatusBarStateController.class, StatusBarStateController::new);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index a90a7d2..ba89fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -140,7 +140,7 @@
createDialogs();
if (!mDialogs.isEmpty()) {
- getComponent(CommandQueue.class).addCallbacks(this);
+ getComponent(CommandQueue.class).addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 416cc59..1e91ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -32,6 +32,7 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -72,7 +73,8 @@
private Point mDisplaySize;
// Bubbles get added to the status bar view
- private StatusBarWindowController mStatusBarWindowController;
+ @VisibleForTesting
+ protected StatusBarWindowController mStatusBarWindowController;
// Used for determining view rect for touch interaction
private Rect mTempRect = new Rect();
@@ -302,6 +304,11 @@
return mTempRect;
}
+ @VisibleForTesting
+ public BubbleStackView getStackView() {
+ return mStackView;
+ }
+
// TODO: factor in PIP location / maybe last place user had it
/**
* Gets an appropriate starting point to position the bubble stack.
@@ -342,13 +349,16 @@
}
}
}
+ boolean isCall = Notification.CATEGORY_CALL.equals(n.getNotification().category)
+ && n.isOngoing();
+ boolean isMusic = n.getNotification().hasMediaSession();
+ boolean isImportantOngoing = isMusic || isCall;
Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
- boolean isMessageType = Notification.MessagingStyle.class.equals(style)
- || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category)
- || hasRemoteInput;
- return (isMessageType && autoBubbleMessages)
- || (n.isOngoing() && autoBubbleOngoing)
+ boolean isMessageType = Notification.CATEGORY_MESSAGE.equals(n.getNotification().category);
+ boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
+ return (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages)
+ || (isImportantOngoing && autoBubbleOngoing)
|| autoBubbleAll;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
new file mode 100644
index 0000000..e28d96b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.ShapeDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Container for the expanded bubble view, handles rendering the caret and header of the view.
+ */
+public class BubbleExpandedViewContainer extends LinearLayout {
+
+ // The triangle pointing to the expanded view
+ private View mPointerView;
+ // The view that is being displayed for the expanded state
+ private View mExpandedView;
+
+ public BubbleExpandedViewContainer(Context context) {
+ this(context, null);
+ }
+
+ public BubbleExpandedViewContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setOrientation(VERTICAL);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ Resources res = getResources();
+ mPointerView = findViewById(R.id.pointer_view);
+ int width = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
+ int height = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
+ ShapeDrawable triangleDrawable = new ShapeDrawable(
+ TriangleShape.create(width, height, true /* pointUp */));
+ triangleDrawable.setTint(Color.WHITE); // TODO: dark mode
+ mPointerView.setBackground(triangleDrawable);
+ }
+
+ /**
+ * Set the x position that the tip of the triangle should point to.
+ */
+ public void setPointerPosition(int x) {
+ // Adjust for the pointer size
+ x -= (mPointerView.getWidth() / 2);
+ mPointerView.setTranslationX(x);
+ }
+
+ /**
+ * Set the view to display for the expanded state. Passing null will clear the view.
+ */
+ public void setExpandedView(View view) {
+ if (mExpandedView != null) {
+ removeView(mExpandedView);
+ }
+ mExpandedView = view;
+ if (mExpandedView != null) {
+ addView(mExpandedView);
+ }
+ }
+
+ /**
+ * @return the view containing the expanded content, can be null.
+ */
+ @Nullable
+ public View getExpandedView() {
+ return mExpandedView;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e395c4c..dfd18b2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -25,6 +25,7 @@
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.RectF;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -52,7 +53,7 @@
private Point mDisplaySize;
private FrameLayout mBubbleContainer;
- private FrameLayout mExpandedViewContainer;
+ private BubbleExpandedViewContainer mExpandedViewContainer;
private int mBubbleSize;
private int mBubblePadding;
@@ -111,7 +112,9 @@
int padding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
- mExpandedViewContainer = new FrameLayout(context);
+ mExpandedViewContainer = (BubbleExpandedViewContainer)
+ LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view,
+ this /* parent */, false /* attachToRoot */);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setPadding(padding, padding, padding, padding);
mExpandedViewContainer.setClipChildren(false);
@@ -390,16 +393,19 @@
ExpandableNotificationRow row = mExpandedBubble.getRowView();
if (!row.equals(mExpandedViewContainer.getChildAt(0))) {
// Different expanded view than what we have
- mExpandedViewContainer.removeAllViews();
+ mExpandedViewContainer.setExpandedView(null);
}
- mExpandedViewContainer.addView(row);
+ int pointerPosition = mExpandedBubble.getPosition().x
+ + (mExpandedBubble.getWidth() / 2);
+ mExpandedViewContainer.setPointerPosition(pointerPosition);
+ mExpandedViewContainer.setExpandedView(row);
}
}
private void applyCurrentState() {
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (!mIsExpanded) {
- mExpandedViewContainer.removeAllViews();
+ mExpandedViewContainer.setExpandedView(null);
} else {
mExpandedViewContainer.setTranslationY(mBubbleContainer.getHeight());
ExpandableNotificationRow row = mExpandedBubble.getRowView();
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
deleted file mode 100644
index 09c000b..0000000
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.car;
-
-import android.content.Context;
-import android.util.ArrayMap;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.car.CarFacetButtonController;
-import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-
-/**
- * Class factory to provide car specific SystemUI components.
- */
-public class CarSystemUIFactory extends SystemUIFactory {
-
- public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
- ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
- return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
- }
-
- @Override
- public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
- Context context) {
- super.injectDependencies(providers, context);
- providers.put(NotificationEntryManager.class,
- () -> new CarNotificationEntryManager(context));
- providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
- providers.put(HvacController.class, () -> new HvacController(context));
- providers.put(NotificationMediaManager.class,
- () -> new CarNotificationMediaManager(context));
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 2c61da3..1718cff 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -108,7 +108,7 @@
UserHandle.USER_ALL);
updateConfiguration();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
}
public static FalsingManager getInstance(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index bf8e04d..7e77843 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -401,7 +401,9 @@
}
mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
event.values);
- updateListener(); // reregister, this sensor only fires once
+ if (!mRegistered) {
+ updateListener(); // reregister, this sensor only fires once
+ }
}));
}
@@ -440,7 +442,9 @@
mRegistered = false;
mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
event.getValues());
- updateListener(); // reregister, this sensor only fires once
+ if (!mRegistered) {
+ updateListener(); // reregister, this sensor only fires once
+ }
}));
};
@@ -487,7 +491,6 @@
}
return sb.append(']').toString();
}
-
}
public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bad0148..afe9a74 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -184,6 +184,7 @@
if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
}
+
if (far && (paused || pausing)) {
if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
mMachine.requestState(DozeMachine.State.DOZE_AOD);
@@ -205,13 +206,13 @@
// In pocket, drop event.
return;
}
- if (pausing || paused) {
+ if (mMachine.getState() == DozeMachine.State.DOZE) {
mMachine.requestState(DozeMachine.State.DOZE_AOD);
}
}, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
} else {
if (!pausing && !paused) {
- mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
+ mMachine.requestState(DozeMachine.State.DOZE);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index aa08562..e8ef454 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -45,7 +45,7 @@
.withCallback(this::onExtensionCallback)
.build();
mPlugin = mExtension.get();
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
}
private void onExtensionCallback(GlobalActions newPlugin) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 0394998..dc11b4c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -55,12 +55,12 @@
mContext = context;
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallback(this);
}
@Override
public void destroy() {
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
if (mGlobalActions != null) {
mGlobalActions.destroy();
mGlobalActions = null;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3b9110d..9ccdf79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -488,6 +488,9 @@
// MVNO SIMs can become transiently NOT_READY when switching networks,
// so we should only lock when they are ABSENT.
onSimAbsentLocked();
+ if (simWasLocked) {
+ resetStateLocked();
+ }
}
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 7792e17..37c8163 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -58,7 +58,7 @@
: com.android.systemui.pip.phone.PipManager.getInstance();
mPipManager.initialize(mContext);
- getComponent(CommandQueue.class).addCallbacks(this);
+ getComponent(CommandQueue.class).addCallback(this);
putComponent(PipUI.class, this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 8262b54..8224365 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -51,6 +51,8 @@
// Used for dismissing a bubble -- bubble should be in the target to be considered a dismiss
private View mTargetView;
+ private int mTargetSlop;
+ private Point mWindowSize;
private int[] mLoc = new int[2];
private boolean mIntersecting;
private Vibrator mVibe;
@@ -69,12 +71,14 @@
// Determine sizes for the view
final Rect stableInsets = new Rect();
WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
- final Point windowSize = new Point();
- mWindowManager.getDefaultDisplay().getRealSize(windowSize);
+ mWindowSize = new Point();
+ mWindowManager.getDefaultDisplay().getRealSize(mWindowSize);
final int gradientHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.pip_dismiss_gradient_height);
final int bottomMargin = mContext.getResources().getDimensionPixelSize(
R.dimen.pip_dismiss_text_bottom_margin);
+ mTargetSlop = mContext.getResources().getDimensionPixelSize(
+ R.dimen.bubble_dismiss_slop);
// Create a new view for the dismiss target
LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -96,7 +100,7 @@
// Add the target to the window
LayoutParams lp = new LayoutParams(
LayoutParams.MATCH_PARENT, gradientHeight,
- 0, windowSize.y - gradientHeight,
+ 0, mWindowSize.y - gradientHeight,
LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
LayoutParams.FLAG_LAYOUT_IN_SCREEN
| LayoutParams.FLAG_NOT_TOUCHABLE
@@ -112,7 +116,7 @@
/**
- * Updates the dismiss target based on location of the view.
+ * Updates the dismiss target based on location of the view, only used for bubbles not for PIP.
*
* @return whether the view is within the dismiss target.
*/
@@ -127,11 +131,13 @@
mTargetView.getLocationOnScreen(mLoc);
Rect targetRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + mTargetView.getWidth(),
mLoc[1] + mTargetView.getHeight());
+ expandRect(targetRect, mTargetSlop);
boolean intersecting = targetRect.intersect(viewRect);
- if (intersecting && !mIntersecting) {
+ if (intersecting != mIntersecting) {
// TODO: is this the right effect?
- mVibe.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- mIntersecting = true;
+ mVibe.vibrate(VibrationEffect.get(intersecting
+ ? VibrationEffect.EFFECT_CLICK
+ : VibrationEffect.EFFECT_TICK));
}
mIntersecting = intersecting;
return intersecting;
@@ -139,7 +145,6 @@
return false;
}
-
/**
* Shows the dismiss target.
*/
@@ -172,4 +177,11 @@
.start();
}
}
+
+ private void expandRect(Rect outRect, int expandAmount) {
+ outRect.left = Math.max(0, outRect.left - expandAmount);
+ outRect.top = Math.max(0, outRect.top - expandAmount);
+ outRect.right = Math.min(mWindowSize.x, outRect.right + expandAmount);
+ outRect.bottom = Math.min(mWindowSize.y, outRect.bottom + expandAmount);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 8495fd3..86ce60d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -224,6 +224,9 @@
// next tap
mTouchState.scheduleDoubleTapTimeoutCallback();
}
+ // Fall through
+ case MotionEvent.ACTION_CANCEL:
+ mTouchState.reset();
break;
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 953eb70..2acbea4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -108,12 +108,12 @@
mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
}
}
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
}
@Override
public void onDestroyView() {
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this);
super.onDestroyView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index e2e943a..d7d3981 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -79,7 +79,7 @@
mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
mFooterIconId = R.drawable.ic_info_outline;
mContext = context;
- mMainHandler = new Handler(Looper.getMainLooper());
+ mMainHandler = new Handler(Looper.myLooper());
mActivityStarter = Dependency.get(ActivityStarter.class);
mSecurityController = Dependency.get(SecurityController.class);
mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index d1c2df5..ca8e824 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -308,7 +308,7 @@
protected static List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
- if (tileList == null) {
+ if (TextUtils.isEmpty(tileList)) {
tileList = res.getString(R.string.quick_settings_tiles);
if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 427f638..3cecff0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -232,6 +232,8 @@
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_OFF);
+ // Don't need to worry about tuner settings for this icon
+ mBatteryRemainingIcon.setIgnoreTunerUpdates(true);
mBatteryRemainingText = findViewById(R.id.batteryRemainingText);
mBatteryRemainingText.setTextColor(fillColor);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 3a96595d..32fd2dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -19,14 +19,19 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
+import android.util.PathParser;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -46,6 +51,7 @@
public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private static final String TAG = "QSTileBaseView";
+ private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
private final H mHandler = new H();
private final int[] mLocInScreen = new int[2];
private final FrameLayout mIconFrame;
@@ -62,6 +68,7 @@
private final int mColorInactive;
private final int mColorDisabled;
private int mCircleColor;
+ private int mBgSize;
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
@@ -71,15 +78,23 @@
super(context);
// Default to Quick Tile padding, and QSTileView will specify its own padding.
int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
-
mIconFrame = new FrameLayout(context);
mIconFrame.setForegroundGravity(Gravity.CENTER);
int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
addView(mIconFrame, new LayoutParams(size, size));
mBg = new ImageView(getContext());
+ Path path = new Path(PathParser.createPathFromPathData(
+ context.getResources().getString(ICON_MASK_ID)));
+ float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+ PathShape p = new PathShape(path, pathSize, pathSize);
+ ShapeDrawable d = new ShapeDrawable(p);
+ int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
+ d.setIntrinsicHeight(bgSize);
+ d.setIntrinsicWidth(bgSize);
mBg.setScaleType(ScaleType.FIT_CENTER);
- mBg.setImageResource(R.drawable.ic_qs_circle);
- mIconFrame.addView(mBg);
+ mBg.setImageDrawable(d);
+ mIconFrame.addView(mBg, ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
mIcon = icon;
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -107,7 +122,7 @@
setFocusable(true);
}
- public View getBgCicle() {
+ public View getBgCircle() {
return mBg;
}
@@ -303,6 +318,7 @@
private class H extends Handler {
private static final int STATE_CHANGED = 1;
+
public H() {
super(Looper.getMainLooper());
}
@@ -314,4 +330,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 451297b..cc27135 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -17,9 +17,10 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CONTEXT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_IS_FULL_QS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -52,6 +53,7 @@
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.statusbar.StatusBarStateController;
import java.util.ArrayList;
@@ -61,6 +63,8 @@
* State management done on a looper provided by the host. Tiles should update state in
* handleUpdateState. Callbacks affecting state should use refreshState to trigger another
* state update pass on tile looper.
+ *
+ * @param <TState> see above
*/
public abstract class QSTileImpl<TState extends State> implements QSTile {
protected final String TAG = "Tile." + getClass().getSimpleName();
@@ -76,6 +80,8 @@
protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
private final ArraySet<Object> mListeners = new ArraySet<>();
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final StatusBarStateController
+ mStatusBarStateController = Dependency.get(StatusBarStateController.class);
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Object mStaleListener = new Object();
@@ -172,17 +178,23 @@
}
public void click() {
- mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)));
+ mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
+ .addTaggedData(FIELD_STATUS_BAR_STATE,
+ mStatusBarStateController.getState())));
mHandler.sendEmptyMessage(H.CLICK);
}
public void secondaryClick() {
- mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)));
+ mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)
+ .addTaggedData(FIELD_STATUS_BAR_STATE,
+ mStatusBarStateController.getState())));
mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
}
public void longClick() {
- mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
+ mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
+ .addTaggedData(FIELD_STATUS_BAR_STATE,
+ mStatusBarStateController.getState())));
mHandler.sendEmptyMessage(H.LONG_CLICK);
Prefs.putInt(
@@ -196,7 +208,7 @@
logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0);
}
return logMaker.setSubtype(getMetricsCategory())
- .addTaggedData(FIELD_CONTEXT, mIsFullQs)
+ .addTaggedData(FIELD_IS_FULL_QS, mIsFullQs)
.addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0702d74..f13b565 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -37,7 +37,7 @@
@Override
public void start() {
- getComponent(CommandQueue.class).addCallbacks(this);
+ getComponent(CommandQueue.class).addCallback(this);
putComponent(Recents.class, this);
mImpl = createRecentsImplementationFromConfig();
mImpl.onStart(mContext, this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8b93995..95019ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -35,6 +35,8 @@
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.policy.CallbackController;
import java.util.ArrayList;
@@ -45,7 +47,7 @@
* coalescing these calls so they don't stack up. For the calls
* are coalesced, note that they are all idempotent.
*/
-public class CommandQueue extends IStatusBar.Stub {
+public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks> {
private static final int INDEX_MASK = 0xffff;
private static final int MSG_SHIFT = 16;
private static final int MSG_MASK = 0xffff << MSG_SHIFT;
@@ -183,12 +185,12 @@
&& !ONLY_CORE_APPS;
}
- public void addCallbacks(Callbacks callbacks) {
+ public void addCallback(Callbacks callbacks) {
mCallbacks.add(callbacks);
callbacks.disable(mDisable1, mDisable2, false /* animate */);
}
- public void removeCallbacks(Callbacks callbacks) {
+ public void removeCallback(Callbacks callbacks) {
mCallbacks.remove(callbacks);
}
@@ -223,7 +225,9 @@
}
}
- public void disable(int state1, int state2) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void disable(int displayId, int state1, int state2) {
disable(state1, state2, true);
}
@@ -266,8 +270,10 @@
}
}
- public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
- int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
synchronized (mLock) {
// Don't coalesce these, since it might have one time flags set such as
// STATUS_BAR_UNHIDE which might get lost.
@@ -282,7 +288,9 @@
}
}
- public void topAppWindowChanged(boolean menuVisible) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void topAppWindowChanged(int displayId, boolean menuVisible) {
synchronized (mLock) {
mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
@@ -290,7 +298,9 @@
}
}
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
@@ -371,7 +381,9 @@
}
}
- public void setWindowState(int window, int state) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void setWindowState(int displayId, int window, int state) {
synchronized (mLock) {
// don't coalesce these
mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
@@ -385,7 +397,9 @@
}
}
- public void appTransitionPending() {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void appTransitionPending(int displayId) {
appTransitionPending(false /* forced */);
}
@@ -395,13 +409,17 @@
}
}
- public void appTransitionCancelled() {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void appTransitionCancelled(int displayId) {
synchronized (mLock) {
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
}
}
- public void appTransitionStarting(long startTime, long duration) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void appTransitionStarting(int displayId, long startTime, long duration) {
appTransitionStarting(startTime, duration, false /* forced */);
}
@@ -412,8 +430,9 @@
}
}
+ // TODO(b/117478341): Add multi-display support.
@Override
- public void appTransitionFinished() {
+ public void appTransitionFinished(int displayId) {
synchronized (mLock) {
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 960d221..7d1b640 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -156,7 +156,7 @@
new IntentFilter(Intent.ACTION_TIME_TICK), null,
Dependency.get(Dependency.TIME_TICK_HANDLER));
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
/**
@@ -167,7 +167,7 @@
*/
public void destroy() {
mContext.unregisterReceiver(mTickReceiver);
- Dependency.get(StatusBarStateController.class).removeListener(this);
+ Dependency.get(StatusBarStateController.class).removeCallback(this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index b0724b1..bba4369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -165,7 +165,7 @@
mCurrentUserId = ActivityManager.getCurrentUser();
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
mLockPatternUtils = new LockPatternUtils(context);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 0e9f950..a5c0a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,9 +15,6 @@
*/
package com.android.systemui.statusbar;
-import android.content.Intent;
-
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -39,12 +36,6 @@
boolean isPresenterFullyCollapsed();
/**
- * Runs the given intent. The presenter may want to run some animations or close itself when
- * this happens.
- */
- void startNotificationGutsIntent(Intent intent, int appUid, ExpandableNotificationRow row);
-
- /**
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
@@ -74,10 +65,9 @@
int getMaxNotificationsWhileLocked(boolean recompute);
/**
- * True if the presenter
- * @return
+ * True if the presenter is currently locked.
*/
- default boolean isPresenterLocked() { return false; }
+ boolean isPresenterLocked();
/**
* Called when the row states are updated by {@link NotificationViewHierarchyManager}.
@@ -88,22 +78,4 @@
* @return true if the shade is collapsing.
*/
boolean isCollapsing();
-
- /**
- * @return true if the shade is collapsing to show an activity over the lock screen
- */
- default public boolean isCollapsingToShowActivityOverLockscreen() {
- return false;
- }
-
- /**
- * Get the {@link ActivityLaunchAnimator} from the presenter so it can be queried by
- * {@link com.android.systemui.statusbar.phone.StatusBar}
- * @return the current animator
- * @deprecated This is only here for now because StatusBar is still the ActivityLaunchAnimator
- * callback but shouldn't be.
- */
- default public ActivityLaunchAnimator getActivityLaunchAnimator() {
- return null;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 7be5461..9391737 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -111,7 +111,6 @@
private final KeyguardManager mKeyguardManager;
protected RemoteInputController mRemoteInputController;
- protected NotificationPresenter mPresenter;
protected NotificationLifetimeExtender.NotificationSafeToRemoveCallback
mNotificationLifetimeFinishedCallback;
protected IStatusBarService mBarService;
@@ -141,7 +140,7 @@
ActivityManager.getService().resumeAppSwitches();
} catch (RemoteException e) {
}
- return mCallback.handleRemoteViewClick(pendingIntent, () -> {
+ return mCallback.handleRemoteViewClick(view, pendingIntent, () -> {
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
options.second.setLaunchWindowingMode(
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
@@ -219,88 +218,7 @@
return false;
}
- ViewParent p = view.getParent();
- RemoteInputView riv = null;
- while (p != null) {
- if (p instanceof View) {
- View pv = (View) p;
- if (pv.isRootNamespace()) {
- riv = findRemoteInputView(pv);
- break;
- }
- }
- p = p.getParent();
- }
- ExpandableNotificationRow row = null;
- while (p != null) {
- if (p instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) p;
- break;
- }
- p = p.getParent();
- }
-
- if (row == null) {
- return false;
- }
-
- row.setUserExpanded(true);
-
- if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
- final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
- if (mLockscreenUserManager.isLockscreenPublicMode(userId)) {
- mCallback.onLockedRemoteInput(row, view);
- return true;
- }
- if (mUserManager.getUserInfo(userId).isManagedProfile()
- && mKeyguardManager.isDeviceLocked(userId)) {
- mCallback.onLockedWorkRemoteInput(userId, row, view);
- return true;
- }
- }
-
- if (riv == null) {
- riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
- if (riv == null) {
- return false;
- }
- if (!row.getPrivateLayout().getExpandedChild().isShown()) {
- mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
- return true;
- }
- }
-
- int width = view.getWidth();
- if (view instanceof TextView) {
- // Center the reveal on the text which might be off-center from the TextView
- TextView tv = (TextView) view;
- if (tv.getLayout() != null) {
- int innerWidth = (int) tv.getLayout().getLineWidth(0);
- innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
- width = Math.min(width, innerWidth);
- }
- }
- int cx = view.getLeft() + width / 2;
- int cy = view.getTop() + view.getHeight() / 2;
- int w = riv.getWidth();
- int h = riv.getHeight();
- int r = Math.max(
- Math.max(cx + cy, cx + (h - cy)),
- Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
- riv.setRevealParameters(cx, cy, r);
- riv.setPendingIntent(pendingIntent);
- riv.setRemoteInput(inputs, input);
- riv.focusAnimated();
-
- return true;
- }
-
- private RemoteInputView findRemoteInputView(View v) {
- if (v == null) {
- return null;
- }
- return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+ return activateRemoteInput(view, inputs, input, pendingIntent);
}
};
@@ -320,10 +238,8 @@
mKeyguardManager = context.getSystemService(KeyguardManager.class);
}
- public void setUpWithPresenter(NotificationPresenter presenter,
- Callback callback,
- RemoteInputController.Delegate delegate) {
- mPresenter = presenter;
+ /** Initializes this component with the provided dependencies. */
+ public void setUpWithCallback(Callback callback, RemoteInputController.Delegate delegate) {
mCallback = callback;
mRemoteInputController = new RemoteInputController(delegate);
mRemoteInputController.addCallback(new RemoteInputController.Callback() {
@@ -358,6 +274,102 @@
}
/**
+ * Activates a given {@link RemoteInput}
+ *
+ * @param view The view of the action button or suggestion chip that was tapped.
+ * @param inputs The remote inputs that need to be sent to the app.
+ * @param input The remote input that needs to be activated.
+ * @param pendingIntent The pending intent to be sent to the app.
+ * @return Whether the {@link RemoteInput} was activated.
+ */
+ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
+ PendingIntent pendingIntent) {
+
+ ViewParent p = view.getParent();
+ RemoteInputView riv = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ if (pv.isRootNamespace()) {
+ riv = findRemoteInputView(pv);
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof ExpandableNotificationRow) {
+ row = (ExpandableNotificationRow) p;
+ break;
+ }
+ p = p.getParent();
+ }
+
+ if (row == null) {
+ return false;
+ }
+
+ row.setUserExpanded(true);
+
+ if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
+ final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+ if (mLockscreenUserManager.isLockscreenPublicMode(userId)) {
+ mCallback.onLockedRemoteInput(row, view);
+ return true;
+ }
+ if (mUserManager.getUserInfo(userId).isManagedProfile()
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ mCallback.onLockedWorkRemoteInput(userId, row, view);
+ return true;
+ }
+ }
+
+ if (riv == null) {
+ riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
+ if (riv == null) {
+ return false;
+ }
+ if (!row.getPrivateLayout().getExpandedChild().isShown()) {
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+ return true;
+ }
+ }
+
+ int width = view.getWidth();
+ if (view instanceof TextView) {
+ // Center the reveal on the text which might be off-center from the TextView
+ TextView tv = (TextView) view;
+ if (tv.getLayout() != null) {
+ int innerWidth = (int) tv.getLayout().getLineWidth(0);
+ innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+ width = Math.min(width, innerWidth);
+ }
+ }
+ int cx = view.getLeft() + width / 2;
+ int cy = view.getTop() + view.getHeight() / 2;
+ int w = riv.getWidth();
+ int h = riv.getHeight();
+ int r = Math.max(
+ Math.max(cx + cy, cx + (h - cy)),
+ Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+
+ riv.setRevealParameters(cx, cy, r);
+ riv.setPendingIntent(pendingIntent);
+ riv.setRemoteInput(inputs, input);
+ riv.focusAnimated();
+
+ return true;
+ }
+
+ private RemoteInputView findRemoteInputView(View v) {
+ if (v == null) {
+ return null;
+ }
+ return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+ }
+
+ /**
* Adds all the notification lifetime extenders. Each extender represents a reason for the
* NotificationRemoteInputManager to keep a notification lifetime extended.
*/
@@ -666,11 +678,13 @@
* Performs any special handling for a remote view click. The default behaviour can be
* called through the defaultHandler parameter.
*
+ * @param view
* @param pendingIntent
* @param defaultHandler
* @return true iff the click was handled
*/
- boolean handleRemoteViewClick(PendingIntent pendingIntent, ClickHandler defaultHandler);
+ boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
+ ClickHandler defaultHandler);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index e7b4904..6cec36a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -121,13 +121,13 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(StatusBarStateController.class)
- .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
+ .addCallback(mStateListener, StatusBarStateController.RANK_SHELF);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
}
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
@@ -225,7 +225,7 @@
}
viewState.hasItemsInStableShelf = lastViewState.inShelf;
viewState.hidden = !mAmbientState.isShadeExpanded()
- || mAmbientState.isQsCustomizerShowing();
+ || mAmbientState.isQsCustomizerShowing() || mAmbientState.isFullyDark();
viewState.maxShelfEnd = maxShelfEnd;
} else {
viewState.hidden = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index daa2fd4..dc3a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -129,7 +129,7 @@
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
- Dependency.get(StatusBarStateController.class).addListener(mStatusBarStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
}
public void setUpWithPresenter(NotificationPresenter presenter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index eaf52cb..3f84416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -26,8 +26,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.CallbackController;
import java.lang.annotation.Retention;
import java.util.ArrayList;
@@ -36,7 +38,7 @@
/**
* Tracks and reports on {@link StatusBarState}.
*/
-public class StatusBarStateController {
+public class StatusBarStateController implements CallbackController<StateListener> {
private static final String TAG = "SbStateController";
private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
@@ -228,7 +230,7 @@
return mLastState == StatusBarState.SHADE_LOCKED;
}
- public void addListener(StateListener listener) {
+ public void addCallback(StateListener listener) {
synchronized (mListeners) {
addListenerInternalLocked(listener, Integer.MAX_VALUE);
}
@@ -244,7 +246,7 @@
* StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
* (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
*/
- public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+ public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
synchronized (mListeners) {
addListenerInternalLocked(listener, rank);
}
@@ -264,7 +266,7 @@
mListeners.sort(mComparator);
}
- public void removeListener(StateListener listener) {
+ public void removeCallback(StateListener listener) {
synchronized (mListeners) {
mListeners.removeIf((it) -> it.listener.equals(listener));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
deleted file mode 100644
index bd32856..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package com.android.systemui.statusbar.car;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-
-/**
- * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
- * category. It can also render a indicator impling that there are more options of apps to launch
- * using this component. This is done with a "More icon" currently an arrow as defined in the layout
- * file. The class is to serve as an example.
- * Usage example: A button that allows a user to select a music app and indicate that there are
- * other music apps installed.
- */
-public class CarFacetButton extends LinearLayout {
- private static final String FACET_FILTER_DELIMITER = ";";
- /**
- * Extra information to be sent to a helper to make the decision of what app to launch when
- * clicked.
- */
- private static final String EXTRA_FACET_CATEGORIES = "categories";
- private static final String EXTRA_FACET_PACKAGES = "packages";
- private static final String EXTRA_FACET_ID = "filter_id";
- private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
-
- private Context mContext;
- private AlphaOptimizedImageButton mIcon;
- private AlphaOptimizedImageButton mMoreIcon;
- private boolean mSelected = false;
- private String[] mComponentNames;
- /** App categories that are to be used with this widget */
- private String[] mFacetCategories;
- /** App packages that are allowed to be used with this widget */
- private String[] mFacetPackages;
- private int mIconResourceId;
- /**
- * If defined in the xml this will be the icon that's rendered when the button is marked as
- * selected
- */
- private int mSelectedIconResourceId;
- private boolean mUseMoreIcon = true;
- private float mSelectedAlpha = 1f;
- private float mUnselectedAlpha = 1f;
-
-
- public CarFacetButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- View.inflate(context, R.layout.car_facet_button, this);
-
- // extract custom attributes
- TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
- setupIntents(typedArray);
- setupIcons(typedArray);
- CarFacetButtonController carFacetButtonController = Dependency.get(
- CarFacetButtonController.class);
- carFacetButtonController.addFacetButton(this);
-
- }
-
- /**
- * Reads the custom attributes to setup click handlers for this component.
- */
- private void setupIntents(TypedArray typedArray) {
- String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
- String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
- String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
- String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
- String componentNameString =
- typedArray.getString(R.styleable.CarFacetButton_componentNames);
- try {
- final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
- intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
-
- if (packageString != null) {
- mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
- intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
- }
- if (categoryString != null) {
- mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
- intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
- }
- if (componentNameString != null) {
- mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
- }
-
- setOnClickListener(v -> {
- intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- });
-
- if (longPressIntentString != null) {
- final Intent longPressIntent = Intent.parseUri(longPressIntentString,
- Intent.URI_INTENT_SCHEME);
- setOnLongClickListener(v -> {
- mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT);
- return true;
- });
- }
- } catch (Exception e) {
- throw new RuntimeException("Failed to attach intent", e);
- }
- }
-
-
- private void setupIcons(TypedArray styledAttributes) {
- mSelectedAlpha = styledAttributes.getFloat(
- R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
- mUnselectedAlpha = styledAttributes.getFloat(
- R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
- mIcon = findViewById(R.id.car_nav_button_icon);
- mIcon.setScaleType(ImageView.ScaleType.CENTER);
- mIcon.setClickable(false);
- mIcon.setAlpha(mUnselectedAlpha);
- mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
- mIcon.setImageResource(mIconResourceId);
- mSelectedIconResourceId = styledAttributes.getResourceId(
- R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
-
- mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
- mMoreIcon.setClickable(false);
- mMoreIcon.setAlpha(mSelectedAlpha);
- mMoreIcon.setVisibility(GONE);
- mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
- }
-
- /**
- * @return The app categories the component represents
- */
- public String[] getCategories() {
- if (mFacetCategories == null) {
- return new String[0];
- }
- return mFacetCategories;
- }
-
- /**
- * @return The valid packages that should be considered.
- */
- public String[] getFacetPackages() {
- if (mFacetPackages == null) {
- return new String[0];
- }
- return mFacetPackages;
- }
-
- public String[] getComponentName() {
- if (mComponentNames == null) {
- return new String[0];
- }
- return mComponentNames;
- }
-
- /**
- * Updates the alpha of the icons to "selected" and shows the "More icon"
- * @param selected true if the view must be selected, false otherwise
- */
- public void setSelected(boolean selected) {
- super.setSelected(selected);
- setSelected(selected, selected);
- }
-
- /**
- * Updates the visual state to let the user know if it's been selected.
- * @param selected true if should update the alpha of the icon to selected, false otherwise
- * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
- * is ignored if the attribute useMoreIcon is set to false
- */
- public void setSelected(boolean selected, boolean showMoreIcon) {
- mSelected = selected;
- mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
- mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
- if (mUseMoreIcon) {
- mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f506753..f899863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -299,6 +299,10 @@
return top;
}
+ public int getBottom() {
+ return bottom;
+ }
+
public int getWidth() {
return right - left;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
new file mode 100644
index 0000000..ba1b23b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.content.Intent;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+/**
+ * Component responsible for handling actions on a notification which cause activites to start.
+ * (e.g. clicking on a notification, tapping on the settings icon in the notification guts)
+ */
+public interface NotificationActivityStarter {
+ /** Called when the user clicks on the surface of a notification. */
+ void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row);
+
+ /** Called when the user clicks on a button in the notification guts which fires an intent. */
+ void startNotificationGutsIntent(Intent intent, int appUid,
+ ExpandableNotificationRow row);
+
+ default boolean isCollapsingToShowActivityOverLockscreen() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 3f8583c..f543b46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -588,7 +588,7 @@
}
public boolean areGutsExposed() {
- return row != null && row.getGuts().isExposed();
+ return row != null && row.getGuts() != null && row.getGuts().isExposed();
}
public boolean isChildInGroup() {
@@ -815,9 +815,18 @@
public boolean isHighPriority(StatusBarNotification statusBarNotification) {
if (mRankingMap != null) {
getRanking(statusBarNotification.getKey(), mTmpRanking);
- return mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+ if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
|| statusBarNotification.getNotification().isForegroundService()
- || statusBarNotification.getNotification().hasMediaSession();
+ || statusBarNotification.getNotification().hasMediaSession()) {
+ return true;
+ }
+ if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
+ for (Entry child : mGroupManager.getLogicalChildren(statusBarNotification)) {
+ if (isHighPriority(child.notification)) {
+ return true;
+ }
+ }
+ }
}
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 1616b6d..16a3849 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -143,6 +143,7 @@
protected IStatusBarService mBarService;
private NotificationPresenter mPresenter;
private Callback mCallback;
+ private NotificationActivityStarter mNotificationActivityStarter;
protected PowerManager mPowerManager;
private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
@@ -182,6 +183,10 @@
} else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
row.getNotificationParent().animateTranslateNotification(0);
return;
+ } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
+ // We never want to open the app directly if the user clicks in between
+ // the notifications.
+ return;
}
// Mark notification for one frame.
@@ -193,7 +198,7 @@
mBubbleController.collapseStack();
}
- mCallback.onNotificationClicked(sbn, row);
+ mNotificationActivityStarter.onNotificationClicked(sbn, row);
}
private boolean isMenuVisible(ExpandableNotificationRow row) {
@@ -346,6 +351,11 @@
mOnAppOpsClickListener = mGutsManager::openGuts;
}
+ public void setNotificationActivityStarter(
+ NotificationActivityStarter notificationActivityStarter) {
+ mNotificationActivityStarter = notificationActivityStarter;
+ }
+
public NotificationData getNotificationData() {
return mNotificationData;
}
@@ -952,10 +962,12 @@
// Has a copy of the current UI adjustments.
ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
+ ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
for (NotificationData.Entry entry : entries) {
NotificationUiAdjustment adjustment =
NotificationUiAdjustment.extractFromNotificationEntry(entry);
oldAdjustments.put(entry.key, adjustment);
+ oldImportances.put(entry.key, entry.importance);
}
// Populate notification entries from the new rankings.
@@ -978,6 +990,11 @@
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
}
+ } else if (oldImportances.containsKey(entry.key)
+ && entry.importance != oldImportances.get(entry.key)) {
+ if (entry.rowExists()) {
+ entry.getRow().onNotificationRankingUpdated();
+ }
}
}
@@ -1240,15 +1257,6 @@
*/
void onNotificationRemoved(String key, StatusBarNotification old);
-
- /**
- * Called when a notification is clicked.
- *
- * @param sbn notification that was clicked
- * @param row row for that notification
- */
- void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row);
-
/**
* Called when a new notification and row is created.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 1f48c15..09eb8a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -75,6 +75,6 @@
/** Returns the value of the new interruption model setting. */
public static boolean useNewInterruptionModel(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) != 0;
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) != 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 87313b8..9f02e54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -149,7 +149,7 @@
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
// Not expected to be destroyed, don't need to unsubscribe
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
public void setUpWithContainer(NotificationListContainer listContainer) {
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 d4d45ea..8bed366 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
@@ -328,6 +328,7 @@
private float mTranslationWhenRemoved;
private boolean mWasChildInGroupWhenRemoved;
private int mNotificationColorAmbient;
+ private NotificationInlineImageResolver mImageResolver;
private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
new SystemNotificationAsyncTask();
@@ -588,6 +589,13 @@
updateRippleAllowed();
}
+ /** Called when the notification's ranking was changed (but nothing else changed). */
+ public void onNotificationRankingUpdated() {
+ if (mMenuRow != null) {
+ mMenuRow.onNotificationUpdated(mStatusBarNotification);
+ }
+ }
+
@VisibleForTesting
void updateShelfIconColor() {
StatusBarIconView expandedIcon = mEntry.expandedIcon;
@@ -1090,6 +1098,10 @@
mHeadsUpManager = headsUpManager;
}
+ public HeadsUpManager getHeadsUpManager() {
+ return mHeadsUpManager;
+ }
+
public void setGutsView(MenuItem item) {
if (mGuts != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
((NotificationGuts.GutsContent) item.getGutsView()).setGutsParent(mGuts);
@@ -1614,6 +1626,8 @@
mFalsingManager = FalsingManager.getInstance(context);
mNotificationInflater = new NotificationInflater(this);
mMenuRow = new NotificationMenuRow(mContext);
+ mImageResolver = new NotificationInlineImageResolver(context,
+ new NotificationInlineImageCache());
initDimens();
}
@@ -1650,6 +1664,10 @@
res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
}
+ NotificationInlineImageResolver getImageResolver() {
+ return mImageResolver;
+ }
+
/**
* Resets this view so it can be re-used for an updated notification.
*/
@@ -2001,7 +2019,8 @@
float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
int startClipTopAmount = params.getStartClipTopAmount();
if (mNotificationParent != null) {
- top -= mNotificationParent.getTranslationY();
+ float parentY = mNotificationParent.getTranslationY();
+ top -= parentY;
mNotificationParent.setTranslationZ(translationZ);
int parentStartClipTopAmount = params.getParentStartClipTopAmount();
if (startClipTopAmount != 0) {
@@ -2011,8 +2030,12 @@
mNotificationParent.setClipTopAmount(clipTopAmount);
}
mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
- mNotificationParent.setMinimumHeightForClipping(params.getHeight()
- + mNotificationParent.getActualHeight());
+ float clipBottom = Math.max(params.getBottom(),
+ parentY + mNotificationParent.getActualHeight()
+ - mNotificationParent.getClipBottomAmount());
+ float clipTop = Math.min(params.getTop(), parentY);
+ int minimumHeightForClipping = (int) (clipBottom - clipTop);
+ mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping);
} else if (startClipTopAmount != 0) {
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
setClipTopAmount(clipTopAmount);
@@ -2078,7 +2101,7 @@
@Override
protected boolean shouldClipToActualHeight() {
- return super.shouldClipToActualHeight() && !mExpandAnimationRunning && !mChildIsExpanding;
+ return super.shouldClipToActualHeight() && !mExpandAnimationRunning;
}
@Override
@@ -3122,6 +3145,7 @@
pw.print(", alpha: " + getAlpha());
pw.print(", translation: " + getTranslation());
pw.print(", removed: " + isRemoved());
+ pw.print(", expandAnimationRunning: " + mExpandAnimationRunning);
NotificationContentView showingLayout = getShowingLayout();
pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
pw.println();
@@ -3135,6 +3159,11 @@
pw.println();
pw.println();
if (mIsSummaryWithChildren) {
+ pw.print(" ChildrenContainer");
+ pw.print(" visibility: " + mChildrenContainer.getVisibility());
+ pw.print(", alpha: " + mChildrenContainer.getAlpha());
+ pw.print(", translationY: " + mChildrenContainer.getTranslationY());
+ pw.println();
List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
pw.println(" Children: " + notificationChildren.size());
pw.println(" {");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 0efb130..6aadcb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -77,8 +77,6 @@
protected boolean mShouldTranslateContents;
private boolean mTopAmountRounded;
private float mDistanceToTopRoundness = -1;
- private float mExtraWidthForClipping;
- private int mMinimumHeightForClipping = 0;
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
@@ -219,13 +217,15 @@
return result;
}
+ @Override
public void setExtraWidthForClipping(float extraWidthForClipping) {
- mExtraWidthForClipping = extraWidthForClipping;
+ super.setExtraWidthForClipping(extraWidthForClipping);
invalidate();
}
+ @Override
public void setMinimumHeightForClipping(int minimumHeightForClipping) {
- mMinimumHeightForClipping = minimumHeightForClipping;
+ super.setMinimumHeightForClipping(minimumHeightForClipping);
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 1e8de07..b1fa6a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -21,7 +21,6 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -48,6 +47,8 @@
private int mActualHeight;
protected int mClipTopAmount;
protected int mClipBottomAmount;
+ protected int mMinimumHeightForClipping = 0;
+ protected float mExtraWidthForClipping = 0;
private boolean mDark;
private ArrayList<View> mMatchParentViews = new ArrayList<View>();
private static Rect mClipRect = new Rect();
@@ -58,10 +59,11 @@
private ViewGroup mTransientContainer;
private boolean mInShelf;
private boolean mTransformingInShelf;
- @Nullable private ExpandableViewState mViewState;
+ private final ExpandableViewState mViewState;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mViewState = createExpandableViewState();
}
@Override
@@ -398,14 +400,26 @@
protected void updateClipping() {
if (mClipToActualHeight && shouldClipToActualHeight()) {
int top = getClipTopAmount();
- mClipRect.set(0, top, getWidth(), Math.max(getActualHeight() + getExtraBottomPadding()
- - mClipBottomAmount, top));
+ int bottom = Math.max(Math.max(getActualHeight() + getExtraBottomPadding()
+ - mClipBottomAmount, top), mMinimumHeightForClipping);
+ int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+ mClipRect.set(-halfExtraWidth, top, getWidth() + halfExtraWidth, bottom);
setClipBounds(mClipRect);
} else {
setClipBounds(null);
}
}
+ public void setMinimumHeightForClipping(int minimumHeightForClipping) {
+ mMinimumHeightForClipping = minimumHeightForClipping;
+ updateClipping();
+ }
+
+ public void setExtraWidthForClipping(float extraWidthForClipping) {
+ mExtraWidthForClipping = extraWidthForClipping;
+ updateClipping();
+ }
+
public float getHeaderVisibleAmount() {
return 1.0f;
}
@@ -522,11 +536,6 @@
/** Sets {@link ExpandableViewState} to default state. */
public ExpandableViewState resetViewState() {
- // TODO(http://b/119762423): Move the null check to getViewState().
- if (mViewState == null) {
- mViewState = createExpandableViewState();
- }
-
// initialize with the default values of the view
mViewState.height = getIntrinsicHeight();
mViewState.gone = getVisibility() == View.GONE;
@@ -559,9 +568,7 @@
/** Applies internal {@link ExpandableViewState} to this view. */
public void applyViewState() {
- if (mViewState == null) {
- Log.wtf(TAG, "No child state was found when applying this state to the hostView");
- } else if (!mViewState.gone) {
+ if (!mViewState.gone) {
mViewState.applyToView(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 689d6d5..edd54ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -91,6 +91,7 @@
private SmartReplyConstants mSmartReplyConstants;
private SmartReplyView mExpandedSmartReplyView;
+ private SmartReplyView mHeadsUpSmartReplyView;
private SmartReplyController mSmartReplyController;
private NotificationViewWrapper mContractedWrapper;
@@ -253,6 +254,9 @@
}
if (mHeadsUpChild != null) {
int maxHeight = mHeadsUpHeight;
+ if (mHeadsUpSmartReplyView != null) {
+ maxHeight += mHeadsUpSmartReplyView.getHeightUpperLimit();
+ }
maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
int size = maxHeight;
ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
@@ -955,6 +959,9 @@
if (mExpandedSmartReplyView != null) {
mExpandedSmartReplyView.setBackgroundTintColor(color);
}
+ if (mHeadsUpSmartReplyView != null) {
+ mHeadsUpSmartReplyView.setBackgroundTintColor(color);
+ }
}
public int getVisibleType() {
@@ -1472,6 +1479,10 @@
entry, smartRepliesAndActions.smartReplies.choices.length);
}
}
+ if (mHeadsUpChild != null) {
+ mHeadsUpSmartReplyView =
+ applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, entry);
+ }
}
private SmartReplyView applySmartReplyView(View view,
@@ -1520,7 +1531,8 @@
}
if (smartRepliesAndActions.smartActions != null) {
smartReplyView.addSmartActions(
- smartRepliesAndActions.smartActions, mSmartReplyController, entry);
+ smartRepliesAndActions.smartActions, mSmartReplyController, entry,
+ mContainingNotification.getHeadsUpManager());
}
smartReplyContainer.setVisibility(View.VISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 3dc50ae..2e45527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -42,13 +41,13 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -79,13 +78,13 @@
Dependency.get(StatusBarStateController.class);
private final DeviceProvisionedController mDeviceProvisionedController =
Dependency.get(DeviceProvisionedController.class);
- private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
// which notification is currently being longpress-examined by the user
private NotificationGuts mNotificationGutsExposed;
private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
private NotificationPresenter mPresenter;
+ private NotificationActivityStarter mNotificationActivityStarter;
private NotificationListContainer mListContainer;
private CheckSaveListener mCheckSaveListener;
private OnSettingsClickListener mOnSettingsClickListener;
@@ -94,8 +93,6 @@
public NotificationGutsManager(Context context) {
mContext = context;
- Resources res = context.getResources();
-
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
@@ -109,6 +106,11 @@
mOnSettingsClickListener = onSettingsClick;
}
+ public void setNotificationActivityStarter(
+ NotificationActivityStarter notificationActivityStarter) {
+ mNotificationActivityStarter = notificationActivityStarter;
+ }
+
public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
setExposedGuts(entry.getGuts());
bindGuts(entry.getRow());
@@ -127,7 +129,7 @@
if (channel != null) {
intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
}
- mPresenter.startNotificationGutsIntent(intent, appUid, row);
+ mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
}
protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
@@ -138,12 +140,12 @@
} else {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.fromParts("package", pkg, null));
- mPresenter.startNotificationGutsIntent(intent, uid, row);
+ mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
}
} else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
- mPresenter.startNotificationGutsIntent(intent, uid, row);
+ mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
}
}
@@ -262,7 +264,8 @@
(View v, Intent intent) -> {
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
guts.resetFalsingCheck();
- mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
+ mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
+ row);
};
boolean isForBlockingHelper = row.isBlockingHelperShowing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 70860258..9908049 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -33,6 +33,7 @@
import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
@@ -114,7 +115,7 @@
@InflationFlag
private int mInflationFlags = REQUIRED_INFLATION_FLAGS;
- private static final InflationExecutor EXECUTOR = new InflationExecutor();
+ static final InflationExecutor EXECUTOR = new InflationExecutor();
private final ExpandableNotificationRow mRow;
private boolean mIsLowPriority;
@@ -244,6 +245,10 @@
// Only inflate the ones that are set.
reInflateFlags &= mInflationFlags;
StatusBarNotification sbn = mRow.getEntry().notification;
+
+ // To check if the notification has inline image and preload inline image if necessary.
+ mRow.getImageResolver().preloadImages(sbn.getNotification());
+
AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler);
@@ -520,8 +525,14 @@
}
return;
}
- RemoteViews.OnViewAppliedListener listener
- = new RemoteViews.OnViewAppliedListener() {
+ RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() {
+
+ @Override
+ public void onViewInflated(View v) {
+ if (v instanceof ImageMessageConsumer) {
+ ((ImageMessageConsumer) v).setImageResolver(row.getImageResolver());
+ }
+ }
@Override
public void onViewApplied(View v) {
@@ -697,7 +708,7 @@
&& newView.getPackage() != null
&& newView.getPackage().equals(oldView.getPackage())
&& newView.getLayoutId() == oldView.getLayoutId()
- && !oldView.isReapplyDisallowed());
+ && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED));
}
public void setInflationCallback(InflationCallback callback) {
@@ -851,6 +862,10 @@
mRow.getEntry().onInflationTaskFinished();
mRow.onNotificationUpdated();
mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
+
+ // Notify the resolver that the inflation task has finished,
+ // try to purge unnecessary cached entries.
+ mRow.getImageResolver().purgeCache();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
new file mode 100644
index 0000000..8c8bad2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A cache for inline images of image messages.
+ */
+public class NotificationInlineImageCache implements NotificationInlineImageResolver.ImageCache {
+ private static final String TAG = NotificationInlineImageCache.class.getSimpleName();
+
+ private NotificationInlineImageResolver mResolver;
+ private final ConcurrentHashMap<Uri, PreloadImageTask> mCache;
+
+ public NotificationInlineImageCache() {
+ mCache = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public void setImageResolver(NotificationInlineImageResolver resolver) {
+ mResolver = resolver;
+ }
+
+ @Override
+ public boolean hasEntry(Uri uri) {
+ return mCache.containsKey(uri);
+ }
+
+ @Override
+ public void preload(Uri uri) {
+ PreloadImageTask newTask = new PreloadImageTask(mResolver);
+ newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri);
+ mCache.put(uri, newTask);
+ }
+
+ @Override
+ public Drawable get(Uri uri) {
+ Drawable result = null;
+ try {
+ result = mCache.get(uri).get();
+ } catch (InterruptedException | ExecutionException ex) {
+ Log.d(TAG, "get: Failed get image from " + uri);
+ }
+ return result;
+ }
+
+ @Override
+ public void purge() {
+ Set<Uri> wantedSet = mResolver.getWantedUriSet();
+ mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey()));
+ }
+
+ private static class PreloadImageTask extends AsyncTask<Uri, Void, Drawable> {
+ private final NotificationInlineImageResolver mResolver;
+
+ PreloadImageTask(NotificationInlineImageResolver resolver) {
+ mResolver = resolver;
+ }
+
+ @Override
+ protected Drawable doInBackground(Uri... uris) {
+ Drawable drawable = null;
+ Uri target = uris[0];
+
+ try {
+ drawable = mResolver.resolveImage(target);
+ } catch (IOException ex) {
+ Log.d(TAG, "PreloadImageTask: Resolve failed from " + target);
+ }
+
+ return drawable;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
new file mode 100644
index 0000000..588246f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.android.internal.widget.ImageResolver;
+import com.android.internal.widget.LocalImageResolver;
+import com.android.internal.widget.MessagingMessage;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Custom resolver with built-in image cache for image messages.
+ */
+public class NotificationInlineImageResolver implements ImageResolver {
+ private static final String TAG = NotificationInlineImageResolver.class.getSimpleName();
+
+ private final Context mContext;
+ private final ImageCache mImageCache;
+ private Set<Uri> mWantedUriSet;
+
+ /**
+ * Constructor.
+ * @param context Context.
+ * @param imageCache The implementation of internal cache.
+ */
+ public NotificationInlineImageResolver(Context context, ImageCache imageCache) {
+ mContext = context.getApplicationContext();
+ mImageCache = imageCache;
+
+ if (mImageCache != null) {
+ mImageCache.setImageResolver(this);
+ }
+ }
+
+ /**
+ * Check if this resolver has its internal cache implementation.
+ * @return True if has its internal cache, false otherwise.
+ */
+ public boolean hasCache() {
+ return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
+ }
+
+ /**
+ * To resolve image from specified uri directly.
+ * @param uri Uri of the image.
+ * @return Drawable of the image.
+ * @throws IOException Throws if failed at resolving the image.
+ */
+ Drawable resolveImage(Uri uri) throws IOException {
+ return LocalImageResolver.resolveImage(uri, mContext);
+ }
+
+ @Override
+ public Drawable loadImage(Uri uri) {
+ Drawable result = null;
+ try {
+ result = hasCache() ? mImageCache.get(uri) : resolveImage(uri);
+ } catch (IOException ex) {
+ Log.d(TAG, "loadImage: Can't load image from " + uri);
+ }
+ return result;
+ }
+
+ /**
+ * Resolve the message list from specified notification and
+ * refresh internal cache according to the result.
+ * @param notification The Notification to be resolved.
+ */
+ public void preloadImages(Notification notification) {
+ if (!hasCache()) {
+ return;
+ }
+
+ retrieveWantedUriSet(notification);
+ Set<Uri> wantedSet = getWantedUriSet();
+ wantedSet.forEach(uri -> {
+ if (!mImageCache.hasEntry(uri)) {
+ // The uri is not in the cache, we need trigger a loading task for it.
+ mImageCache.preload(uri);
+ }
+ });
+ }
+
+ /**
+ * Try to purge unnecessary cache entries.
+ */
+ public void purgeCache() {
+ if (!hasCache()) {
+ return;
+ }
+ mImageCache.purge();
+ }
+
+ private void retrieveWantedUriSet(Notification notification) {
+ Parcelable[] messages;
+ Parcelable[] historicMessages;
+ List<Notification.MessagingStyle.Message> messageList;
+ List<Notification.MessagingStyle.Message> historicList;
+ Set<Uri> result = new HashSet<>();
+
+ Bundle extras = notification.extras;
+ if (extras == null) {
+ return;
+ }
+
+ messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+ messageList = messages == null ? null :
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+ if (messageList != null) {
+ for (Notification.MessagingStyle.Message message : messageList) {
+ if (MessagingMessage.hasImage(message)) {
+ result.add(message.getDataUri());
+ }
+ }
+ }
+
+ historicMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
+ historicList = historicMessages == null ? null :
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(historicMessages);
+ if (historicList != null) {
+ for (Notification.MessagingStyle.Message historic : historicList) {
+ if (MessagingMessage.hasImage(historic)) {
+ result.add(historic.getDataUri());
+ }
+ }
+ }
+
+ mWantedUriSet = result;
+ }
+
+ Set<Uri> getWantedUriSet() {
+ return mWantedUriSet;
+ }
+
+ /**
+ * A interface for internal cache implementation of this resolver.
+ */
+ interface ImageCache {
+ /**
+ * Load the image from cache first then resolve from uri if missed the cache.
+ * @param uri The uri of the image.
+ * @return Drawable of the image.
+ */
+ Drawable get(Uri uri);
+
+ /**
+ * Set the image resolver that actually resolves image from specified uri.
+ * @param resolver The resolver implementation that resolves image from specified uri.
+ */
+ void setImageResolver(NotificationInlineImageResolver resolver);
+
+ /**
+ * Check if the uri is in the cache no matter it is loading or loaded.
+ * @param uri The uri to check.
+ * @return True if it is already in the cache; false otherwise.
+ */
+ boolean hasEntry(Uri uri);
+
+ /**
+ * Start a new loading task for the target uri.
+ * @param uri The target to load.
+ */
+ void preload(Uri uri);
+
+ /**
+ * Purge unnecessary entries in the cache.
+ */
+ void purge();
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 948d2a5..d97162c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -30,6 +30,7 @@
import android.os.Handler;
import android.os.Looper;
import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -47,6 +48,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
ExpandableNotificationRow.LayoutListener {
@@ -74,6 +76,7 @@
private MenuItem mSnoozeItem;
private ArrayList<MenuItem> mLeftMenuItems;
private ArrayList<MenuItem> mRightMenuItems;
+ private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
private OnMenuEventListener mMenuListener;
private ValueAnimator mFadeAnimator;
@@ -267,7 +270,9 @@
} else {
mRightMenuItems.add(mInfoItem);
mRightMenuItems.add(mAppOpsItem);
- mRightMenuItems.add(mSnoozeItem);
+ if (!isForeground) {
+ mRightMenuItems.add(mSnoozeItem);
+ }
}
populateMenuViews();
@@ -285,6 +290,7 @@
private void populateMenuViews() {
if (mMenuContainer != null) {
mMenuContainer.removeAllViews();
+ mMenuItemsByView.clear();
} else {
mMenuContainer = new FrameLayout(mContext);
}
@@ -484,10 +490,8 @@
final int centerY = v.getHeight() / 2;
final int x = mIconLocation[0] - mParentLocation[0] + centerX;
final int y = mIconLocation[1] - mParentLocation[1] + centerY;
- final int index = mMenuContainer.indexOfChild(v);
- if (mMenuListener != null) {
- mMenuListener.onMenuClicked(mParent, x, y,
- (mOnLeft ? mLeftMenuItems : mRightMenuItems).get(index));
+ if (mMenuItemsByView.containsKey(v)) {
+ mMenuListener.onMenuClicked(mParent, x, y, mMenuItemsByView.get(v));
}
}
@@ -639,8 +643,8 @@
NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
R.layout.notification_info, null, false);
int iconResId = isCurrentlySilent
- ? R.drawable.ic_notifications_alert
- : R.drawable.ic_notifications_silence;
+ ? R.drawable.ic_notifications_silence
+ : R.drawable.ic_notifications_alert;
return new NotificationMenuItem(context, infoDescription, infoContent, iconResId);
}
@@ -663,6 +667,7 @@
lp.height = mHorizSpaceForIcon;
menuView.setLayoutParams(lp);
}
+ mMenuItemsByView.put(menuView, item);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 0f38bd9..5118036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -875,6 +875,7 @@
ExpandableNotificationRow child = mChildren.get(childIdx);
child.setChildrenExpanded(childrenExpanded, false);
}
+ updateHeaderTouchability();
}
public void setContainingNotification(ExpandableNotificationRow parent) {
@@ -1215,6 +1216,13 @@
ExpandableNotificationRow child = mChildren.get(i);
child.setUserLocked(userLocked && !showingAsLowPriority());
}
+ updateHeaderTouchability();
+ }
+
+ private void updateHeaderTouchability() {
+ if (mNotificationHeader != null) {
+ mNotificationHeader.setAcceptAllTouches(mChildrenExpanded || mUserLocked);
+ }
}
public void onNotificationUpdated() {
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 5d640e0..eca1a14 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
@@ -618,7 +618,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(StatusBarStateController.class)
- .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
+ .addCallback(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -626,7 +626,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Dependency.get(ConfigurationController.class).removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 3b13fe9..24570ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -120,15 +120,15 @@
@Override
public void onResume() {
super.onResume();
- mCommandQueue.addCallbacks(this);
- mStatusBarStateController.addListener(this);
+ mCommandQueue.addCallback(this);
+ mStatusBarStateController.addCallback(this);
}
@Override
public void onPause() {
super.onPause();
- mCommandQueue.removeCallbacks(this);
- mStatusBarStateController.removeListener(this);
+ mCommandQueue.removeCallback(this);
+ mStatusBarStateController.removeCallback(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 94b2cde..cfa751c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -88,7 +88,7 @@
public DozeScrimController(DozeParameters dozeParameters) {
mDozeParameters = dozeParameters;
//Never expected to be destroyed
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
@VisibleForTesting
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 9faada0..aa0b7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -129,7 +129,7 @@
updateTouchableRegionListener();
}
});
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
if (!hasBubbles) {
mBubbleGoingAway = true;
@@ -143,7 +143,7 @@
}
public void destroy() {
- Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
}
private void initResources() {
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 b29889d..57cc7d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -73,15 +73,15 @@
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
SysUiServiceProvider.getComponent(context, CommandQueue.class)
- .addCallbacks(this);
- mStatusBarStateController.addListener(this);
+ .addCallback(this);
+ mStatusBarStateController.addCallback(this);
mDozeAmount = mStatusBarStateController.getDozeAmount();
}
public void destroy(Context context) {
SysUiServiceProvider.getComponent(context, CommandQueue.class)
- .removeCallbacks(this);
- mStatusBarStateController.removeListener(this);
+ .removeCallback(this);
+ mStatusBarStateController.removeCallback(this);
}
public void saveState(Bundle outState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
index b83ebc7..9c8b1b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -56,6 +56,11 @@
}
@Override
+ public boolean allowHitTargetToMoveOverDrag() {
+ return true;
+ }
+
+ @Override
public boolean canPerformAction() {
return mProxySender.getBackButtonAlpha() > 0;
}
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 8657003..ae0a14529 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -32,7 +32,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.Fragment;
import android.app.IActivityTaskManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
@@ -90,6 +89,7 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyButtonView;
+import com.android.systemui.util.LifecycleFragment;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -101,7 +101,7 @@
* Fragment containing the NavigationBarFragment. Contains logic for what happens
* on clicks and view states of the nav bar.
*/
-public class NavigationBarFragment extends Fragment implements Callbacks {
+public class NavigationBarFragment extends LifecycleFragment implements Callbacks {
public static final String TAG = "NavigationBar";
private static final boolean DEBUG = false;
@@ -199,7 +199,7 @@
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
- mCommandQueue.addCallbacks(this);
+ mCommandQueue.observe(getLifecycle(), this);
mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
@@ -225,7 +225,6 @@
@Override
public void onDestroy() {
super.onDestroy();
- mCommandQueue.removeCallbacks(this);
Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
mAccessibilityListener);
mContentResolver.unregisterContentObserver(mMagnificationObserver);
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 12a0cc8..3984405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -24,7 +24,6 @@
import android.view.Display;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowManager;
-import android.view.MotionEvent;
import android.view.View;
import com.android.internal.statusbar.IStatusBarService;
@@ -165,23 +164,4 @@
}
mView.onDarkIntensityChange(darkIntensity);
}
-
- private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- // even though setting the systemUI visibility below will turn these views
- // on, we need them to come up faster so that they can catch this motion
- // event
- applyLightsOut(false, false, false);
-
- try {
- mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
- "LightsOutListener");
- } catch (android.os.RemoteException ex) {
- }
- }
- return false;
- }
- };
}
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 cd6e1d7..30e8409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -63,6 +63,7 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.Interpolators;
@@ -96,7 +97,6 @@
final static boolean ALTERNATE_CAR_MODE_UI = false;
- final Display mDisplay;
View mCurrentView = null;
View[] mRotatedViews = new View[4];
@@ -154,6 +154,7 @@
private QuickScrubAction mQuickScrubAction;
private QuickStepAction mQuickStepAction;
private NavigationBackAction mBackAction;
+ private QuickSwitchAction mQuickSwitchAction;
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -212,8 +213,8 @@
private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
- mContext.getSystemService(InputMethodManager.class)
- .showInputMethodPicker(true /* showAuxiliarySubtypes */);
+ mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
+ true /* showAuxiliarySubtypes */, getContext().getDisplayId());
}
};
@@ -281,8 +282,6 @@
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
- mDisplay = context.getDisplay();
-
mVertical = false;
mLongClickableAccessibilityButton = false;
@@ -326,9 +325,10 @@
mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
mBackAction = new NavigationBackAction(this, mOverviewProxyService);
+ mQuickSwitchAction = new QuickSwitchAction(this, mOverviewProxyService);
mDefaultGestureMap = new NavigationGestureAction[] {
mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
- mQuickScrubAction
+ mQuickScrubAction, null /* swipeLeftEdgeAction */, null /* swipeRightEdgeAction */
};
mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
@@ -359,7 +359,9 @@
getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
- getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]));
+ getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]),
+ getNavigationActionFromType(assignedMap[4], mDefaultGestureMap[4]),
+ getNavigationActionFromType(assignedMap[5], mDefaultGestureMap[5]));
}
}
@@ -372,6 +374,8 @@
return mQuickScrubAction;
case NavigationPrototypeController.ACTION_BACK:
return mBackAction;
+ case NavigationPrototypeController.ACTION_QUICKSWITCH:
+ return mQuickSwitchAction;
default:
return defaultAction;
}
@@ -652,8 +656,8 @@
Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
+ " mDisabledFlags=" + mDisabledFlags);
- // Always disable recents when alternate car mode UI is active.
- boolean disableRecent = mUseCarModeUi || !isOverviewEnabled();
+ // Always disable recents when alternate car mode UI is active and for secondary displays.
+ boolean disableRecent = isRecentsButtonDisabled();
boolean disableBack = QuickStepController.shouldhideBackButton(getContext())
|| (((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) && !useAltBack);
@@ -689,6 +693,16 @@
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
}
+ @VisibleForTesting
+ boolean isRecentsButtonDisabled() {
+ return mUseCarModeUi || !isOverviewEnabled()
+ || getContext().getDisplayId() != Display.DEFAULT_DISPLAY;
+ }
+
+ private Display getContextDisplay() {
+ return getContext().getDisplay();
+ }
+
public boolean inScreenPinning() {
return ActivityManagerWrapper.getInstance().isScreenPinningActive();
}
@@ -890,7 +904,7 @@
}
private void updateCurrentView() {
- final int rot = mDisplay.getRotation();
+ final int rot = getContextDisplay().getRotation();
for (int i=0; i<4; i++) {
mRotatedViews[i].setVisibility(View.GONE);
}
@@ -954,7 +968,7 @@
int navBarPos = NAV_BAR_INVALID;
try {
navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(
- mDisplay.getDisplayId());
+ getContext().getDisplayId());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get nav bar position.", e);
}
@@ -1128,7 +1142,7 @@
pw.println("NavigationBarView {");
final Rect r = new Rect();
final Point size = new Point();
- mDisplay.getRealSize(size);
+ getContextDisplay().getRealSize(size);
pw.println(String.format(" this: " + StatusBar.viewInfo(this)
+ " " + visibilityToString(getVisibility())));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index a8d00c4..8c57fc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -112,7 +112,7 @@
/**
* @return whether or not to move the button that started gesture over with user input drag
*/
- public boolean requiresDragWithHitTarget() {
+ public boolean allowHitTargetToMoveOverDrag() {
return false;
}
@@ -139,9 +139,9 @@
* Tell if action is enabled. Compared to {@link #canPerformAction()} this is based on settings
* if the action is disabled for a particular gesture. For example a back action can be enabled
* however if there is nothing to back to then {@link #canPerformAction()} should return false.
- * In this way if the action requires {@link #requiresDragWithHitTarget()} then if enabled, the
- * button can be dragged with a large dampening factor during the gesture but will not activate
- * the action.
+ * In this way if the action requires {@link #allowHitTargetToMoveOverDrag()} then if enabled,
+ * the button can be dragged with a large dampening factor during the gesture but will not
+ * activate the action.
* @return true if this action is enabled and can run
*/
public abstract boolean isEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index e8c0bf1..b11b6d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -24,7 +24,6 @@
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
-import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -46,6 +45,7 @@
static final int ACTION_QUICKSTEP = 1;
static final int ACTION_QUICKSCRUB = 2;
static final int ACTION_BACK = 3;
+ static final int ACTION_QUICKSWITCH = 4;
private OnPrototypeChangedListener mListener;
@@ -53,7 +53,7 @@
* Each index corresponds to a different action set in QuickStepController
* {@see updateSwipeLTRBackSetting}
*/
- private int[] mActionMap = new int[4];
+ private int[] mActionMap = new int[6];
private final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3e31fa0..2a68fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -82,7 +82,7 @@
private boolean mIsDozing;
public NotificationGroupAlertTransferHelper() {
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 448b5c3..8f4369a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -55,7 +55,7 @@
private boolean mIsUpdatingUnchangedGroup;
public NotificationGroupManager() {
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 33d176a..a2a11bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -366,7 +366,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
Dependency.get(ZenModeController.class).addCallback(this);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -375,7 +375,7 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).removeListener(this);
+ Dependency.get(StatusBarStateController.class).removeCallback(this);
Dependency.get(ZenModeController.class).removeCallback(this);
Dependency.get(ConfigurationController.class).removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index c84f3db..ee1eb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -268,7 +268,7 @@
mLocationController.addCallback(this);
mPrivacyItemController.setListening(true);
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
// Clear out all old notifications on startup (only present in the case where sysui dies)
@@ -296,7 +296,7 @@
mKeyguardMonitor.removeCallback(this);
mLocationController.removeCallback(this);
mPrivacyItemController.setListening(false);
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
mContext.unregisterReceiver(mIntentReceiver);
NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
index 2b202eb..bbfd51a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
@@ -18,8 +18,6 @@
import static com.android.systemui.Interpolators.ALPHA_IN;
import static com.android.systemui.Interpolators.ALPHA_OUT;
-import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -31,11 +29,8 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RadialGradient;
-import android.graphics.Rect;
import android.graphics.Shader;
-import android.os.RemoteException;
import android.util.FloatProperty;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -46,7 +41,7 @@
/**
* QuickScrub action to send to launcher to start quickscrub gesture
*/
-public class QuickScrubAction extends NavigationGestureAction {
+public class QuickScrubAction extends QuickSwitchAction {
private static final String TAG = "QuickScrubAction";
private static final float TRACK_SCALE = 0.95f;
@@ -65,7 +60,6 @@
private final int mTrackThickness;
private final int mTrackEndPadding;
private final Paint mTrackPaint = new Paint();
- private final Rect mTrackRect = new Rect();
private final FloatProperty<QuickScrubAction> mTrackAlphaProperty =
new FloatProperty<QuickScrubAction>("TrackAlpha") {
@@ -177,7 +171,7 @@
x1 = mNavigationBarView.getPaddingStart() + mTrackEndPadding;
x2 = x1 + width - 2 * mTrackEndPadding;
}
- mTrackRect.set(x1, y1, x2, y2);
+ mDragOverRect.set(x1, y1, x2, y2);
}
@Override
@@ -194,15 +188,16 @@
mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
// Scale the track, but apply the inverse scale from the nav bar
- final float radius = mTrackRect.height() / 2;
+ final float radius = mDragOverRect.height() / 2;
canvas.save();
- float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
+ float translate = Utilities.clamp(mHighlightCenter, mDragOverRect.left,
+ mDragOverRect.right);
canvas.translate(translate, 0);
canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
1f / mNavigationBarView.getScaleY(),
- mTrackRect.centerX(), mTrackRect.centerY());
- canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
- mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
+ mDragOverRect.centerX(), mDragOverRect.centerY());
+ canvas.drawRoundRect(mDragOverRect.left - translate, mDragOverRect.top,
+ mDragOverRect.right - translate, mDragOverRect.bottom, radius, radius, mTrackPaint);
canvas.restore();
}
@@ -212,11 +207,6 @@
}
@Override
- public boolean disableProxyEvents() {
- return true;
- }
-
- @Override
protected void onGestureStart(MotionEvent event) {
updateHighlight();
ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
@@ -231,42 +221,12 @@
mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
mTrackAnimator.start();
- // Disable slippery for quick scrub to not cancel outside the nav bar
- mNavigationBarView.updateSlippery();
-
- try {
- mProxySender.getProxy().onQuickScrubStart();
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub Start");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send start of quick scrub.", e);
- }
- mProxySender.notifyQuickScrubStarted();
+ startQuickGesture(event);
}
@Override
public void onGestureMove(int x, int y) {
- int trackSize, offset;
- if (isNavBarVertical()) {
- trackSize = mTrackRect.height();
- offset = y - mTrackRect.top;
- } else {
- offset = x - mTrackRect.left;
- trackSize = mTrackRect.width();
- }
- if (!mDragHorizontalPositive || !mDragVerticalPositive) {
- offset -= isNavBarVertical() ? mTrackRect.height() : mTrackRect.width();
- }
- float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
- try {
- mProxySender.getProxy().onQuickScrubProgress(scrubFraction);
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send progress of quick scrub.", e);
- }
+ super.onGestureMove(x, y);
mHighlightCenter = x;
mNavigationBarView.invalidate();
}
@@ -278,14 +238,7 @@
private void endQuickScrub(boolean animate) {
animateEnd();
- try {
- mProxySender.getProxy().onQuickScrubEnd();
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub End");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send end of quick scrub.", e);
- }
+ endQuickGesture(animate);
if (!animate) {
if (mTrackAnimator != null) {
mTrackAnimator.end();
@@ -295,7 +248,7 @@
}
private void updateHighlight() {
- if (mTrackRect.isEmpty()) {
+ if (mDragOverRect.isEmpty()) {
return;
}
int colorBase, colorGrad;
@@ -306,8 +259,8 @@
colorBase = getContext().getColor(R.color.quick_step_track_background_background_light);
colorGrad = getContext().getColor(R.color.quick_step_track_background_foreground_light);
}
- final RadialGradient mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
- mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
+ final RadialGradient mHighlight = new RadialGradient(0, mDragOverRect.height() / 2,
+ mDragOverRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
Shader.TileMode.CLAMP);
mTrackPaint.setShader(mHighlight);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 2cbf27c..0cec637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -76,7 +76,9 @@
private static final int ACTION_SWIPE_DOWN_INDEX = 1;
private static final int ACTION_SWIPE_LEFT_INDEX = 2;
private static final int ACTION_SWIPE_RIGHT_INDEX = 3;
- private static final int MAX_GESTURES = 4;
+ private static final int ACTION_SWIPE_LEFT_FROM_EDGE_INDEX = 4;
+ private static final int ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX = 5;
+ private static final int MAX_GESTURES = 6;
private NavigationBarView mNavigationBarView;
@@ -97,6 +99,7 @@
private float mMaxDragLimit;
private float mMinDragLimit;
private float mDragDampeningFactor;
+ private float mEdgeSwipeThreshold;
private NavigationGestureAction mCurrentAction;
private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
@@ -128,15 +131,21 @@
* @param swipeDownAction action after swiping down
* @param swipeLeftAction action after swiping left
* @param swipeRightAction action after swiping right
+ * @param swipeLeftFromEdgeAction action swiping left starting from the right side
+ * @param swipeRightFromEdgeAction action swiping right starting from the left side
*/
public void setGestureActions(@Nullable NavigationGestureAction swipeUpAction,
@Nullable NavigationGestureAction swipeDownAction,
@Nullable NavigationGestureAction swipeLeftAction,
- @Nullable NavigationGestureAction swipeRightAction) {
+ @Nullable NavigationGestureAction swipeRightAction,
+ @Nullable NavigationGestureAction swipeLeftFromEdgeAction,
+ @Nullable NavigationGestureAction swipeRightFromEdgeAction) {
mGestureActions[ACTION_SWIPE_UP_INDEX] = swipeUpAction;
mGestureActions[ACTION_SWIPE_DOWN_INDEX] = swipeDownAction;
mGestureActions[ACTION_SWIPE_LEFT_INDEX] = swipeLeftAction;
mGestureActions[ACTION_SWIPE_RIGHT_INDEX] = swipeRightAction;
+ mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] = swipeLeftFromEdgeAction;
+ mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] = swipeRightFromEdgeAction;
// Set the current state to all actions
for (NavigationGestureAction action: mGestureActions) {
@@ -185,6 +194,7 @@
// Requires proxy and an active gesture or able to perform any gesture to continue
if (mOverviewEventSender.getProxy() == null
+ || !mOverviewEventSender.shouldShowSwipeUpUI()
|| (mCurrentAction == null && !canPerformAnyAction())) {
return deadZoneConsumed;
}
@@ -232,6 +242,8 @@
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
mAllowGestureDetection = true;
mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
+ mEdgeSwipeThreshold = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold);
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -283,13 +295,19 @@
}
} else if (exceededSwipeHorizontalTouchSlop) {
if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
- // Swiping left (ltr) gesture
- tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
- true /* alignedWithNavBar */, event);
+ // Swiping left (rtl) gesture
+ int index = mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] != null
+ && isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive)
+ ? ACTION_SWIPE_LEFT_FROM_EDGE_INDEX : ACTION_SWIPE_LEFT_INDEX;
+ tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
+ event);
} else {
// Swiping right (ltr) gesture
- tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
- true /* alignedWithNavBar */, event);
+ int index = mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] != null
+ && isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive)
+ ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX : ACTION_SWIPE_RIGHT_INDEX;
+ tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
+ event);
}
}
}
@@ -332,6 +350,17 @@
return mCurrentAction != null || deadZoneConsumed;
}
+ private boolean isEdgeSwipeAlongNavBar(int touchDown, boolean dragPositiveDirection) {
+ // Detect edge swipe from side of 0 -> threshold
+ if (dragPositiveDirection) {
+ return touchDown < mEdgeSwipeThreshold;
+ }
+ // Detect edge swipe from side of size -> (size - threshold)
+ final int largeSide = isNavBarVertical()
+ ? mNavigationBarView.getHeight() : mNavigationBarView.getWidth();
+ return touchDown > largeSide - mEdgeSwipeThreshold;
+ }
+
private void handleDragHitTarget(int position, int touchDown) {
// Drag the hit target if gesture action requires it
if (mHitTarget != null && (mGestureVerticalDragsButton || mGestureHorizontalDragsButton)) {
@@ -479,7 +508,7 @@
event.transform(mTransformLocalMatrix);
// Calculate the bounding limits of drag to avoid dragging off nav bar's window
- if (action.requiresDragWithHitTarget() && mHitTarget != null) {
+ if (action.allowHitTargetToMoveOverDrag() && mHitTarget != null) {
final int[] buttonCenter = new int[2];
View button = mHitTarget.getCurrentView();
button.getLocationInWindow(buttonCenter);
@@ -504,7 +533,7 @@
// Handle direction of the hit target drag from the axis that started the gesture
// Also calculate the dampening factor, weaker dampening if there is an active action
- if (action.requiresDragWithHitTarget()) {
+ if (action.allowHitTargetToMoveOverDrag()) {
if (alignedWithNavBar) {
mGestureHorizontalDragsButton = true;
mGestureVerticalDragsButton = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java
new file mode 100644
index 0000000..40f2392
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
+import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.utilities.Utilities;
+
+/**
+ * QuickSwitch action to send to launcher
+ */
+public class QuickSwitchAction extends NavigationGestureAction {
+ private static final String TAG = "QuickSwitchAction";
+ private static final String QUICKSWITCH_ENABLED_SETTING = "QUICK_SWITCH";
+
+ protected final Rect mDragOverRect = new Rect();
+
+ public QuickSwitchAction(@NonNull NavigationBarView navigationBar,
+ @NonNull OverviewProxyService service) {
+ super(navigationBar, service);
+ }
+
+ @Override
+ public void setBarState(boolean changed, int navBarPos, boolean dragHorPositive,
+ boolean dragVerPositive) {
+ super.setBarState(changed, navBarPos, dragHorPositive, dragVerPositive);
+ if (changed && isActive()) {
+ // End quickscrub if the state changes mid-transition
+ endQuickGesture(false /* animate */);
+ }
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mNavigationBarView.isQuickScrubEnabled();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mDragOverRect.set(top, left, right, bottom);
+ }
+
+ @Override
+ public boolean disableProxyEvents() {
+ return true;
+ }
+
+ @Override
+ protected void onGestureStart(MotionEvent event) {
+ // Temporarily enable launcher to allow quick switch instead of quick scrub
+ Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
+ QUICKSWITCH_ENABLED_SETTING, 1 /* enabled */);
+
+ startQuickGesture(event);
+ }
+
+ @Override
+ public void onGestureMove(int x, int y) {
+ int dragLength, offset;
+ if (isNavBarVertical()) {
+ dragLength = mDragOverRect.height();
+ offset = y - mDragOverRect.top;
+ } else {
+ offset = x - mDragOverRect.left;
+ dragLength = mDragOverRect.width();
+ }
+ if (!mDragHorizontalPositive || !mDragVerticalPositive) {
+ offset -= dragLength;
+ }
+ float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / dragLength, 0, 1);
+ try {
+ mProxySender.getProxy().onQuickScrubProgress(scrubFraction);
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Quick Switch Progress:" + scrubFraction);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send progress of quick switch.", e);
+ }
+ }
+
+ @Override
+ protected void onGestureEnd() {
+ endQuickGesture(true /* animate */);
+
+ // Disable launcher to use quick switch instead of quick scrub
+ Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
+ QUICKSWITCH_ENABLED_SETTING, 0 /* disabled */);
+ }
+
+ protected void startQuickGesture(MotionEvent event) {
+ // Disable slippery for quick scrub to not cancel outside the nav bar
+ mNavigationBarView.updateSlippery();
+
+ try {
+ mProxySender.getProxy().onQuickScrubStart();
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Quick Scrub Start");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send start of quick scrub.", e);
+ }
+ mProxySender.notifyQuickScrubStarted();
+ }
+
+ protected void endQuickGesture(boolean animate) {
+ try {
+ mProxySender.getProxy().onQuickScrubEnd();
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Quick Scrub End");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send end of quick scrub.", e);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index cfd53be..1d6a1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -121,8 +121,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.widget.MessagingGroup;
-import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
@@ -161,7 +159,6 @@
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -188,6 +185,7 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -474,8 +472,10 @@
return;
}
WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
- final boolean supportsAmbientMode = info != null &&
- info.supportsAmbientMode();
+ final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+ final boolean supportsAmbientMode = deviceSupportsAodWallpaper
+ && info != null && info.supportsAmbientMode();
mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -577,7 +577,10 @@
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private boolean mVibrateOnOpening;
private VibratorHelper mVibratorHelper;
+ private ActivityLaunchAnimator mActivityLaunchAnimator;
protected NotificationPresenter mPresenter;
+ private NotificationActivityStarter mNotificationActivityStarter;
+ private boolean mPulsing;
@Override
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -626,7 +629,7 @@
mBubbleController.setExpandListener(mBubbleExpandListener);
mColorExtractor.addOnColorsChangedListener(this);
- mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
+ mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(
@@ -662,7 +665,7 @@
// Connect in to the status bar manager service
mCommandQueue = getComponent(CommandQueue.class);
- mCommandQueue.addCallbacks(this);
+ mCommandQueue.addCallback(this);
int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<>();
@@ -918,8 +921,7 @@
Dependency.get(ExtensionController.class)
.newExtension(QS.class)
.withPlugin(QS.class)
- .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
- .withDefault(QSFragment::new)
+ .withDefault(this::createDefaultQSFragment)
.build());
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController);
@@ -1007,15 +1009,29 @@
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
}
+ protected QS createDefaultQSFragment() {
+ return new QSFragment();
+ }
+
protected void setUpPresenter() {
// Set up the initial notification state.
+ mActivityLaunchAnimator = new ActivityLaunchAnimator(
+ mStatusBarWindow, this, mNotificationPanel,
+ (NotificationListContainer) mStackScroller);
+
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
- mScrimController, this);
+ mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager);
+
mAppOpsController.addCallback(APP_OPS, this);
mNotificationListener.setUpWithPresenter(mPresenter);
mNotificationShelf.setOnActivatedListener(mPresenter);
mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
+
+ mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
+ mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
+ mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
+ mEntryManager.setNotificationActivityStarter(mNotificationActivityStarter);
}
/**
@@ -1098,8 +1114,6 @@
@Override
public void onDensityOrFontScaleChanged() {
- MessagingMessage.dropCache();
- MessagingGroup.dropCache();
// TODO: Remove this.
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
@@ -1540,7 +1554,7 @@
}
public boolean isPulsing() {
- return mAmbientPulseManager.hasNotifications();
+ return mPulsing;
}
public boolean isLaunchTransitionFadingAway() {
@@ -1634,9 +1648,9 @@
@Override
public void onExpandAnimationTimedOut() {
- ActivityLaunchAnimator animator = mPresenter.getActivityLaunchAnimator();
if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
- && animator != null && !animator.isLaunchForActivity()) {
+ && mActivityLaunchAnimator != null
+ && !mActivityLaunchAnimator.isLaunchForActivity()) {
onClosingFinished();
} else {
collapsePanel(true /* animate */);
@@ -1935,7 +1949,7 @@
runPostCollapseRunnables();
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
- if (!mPresenter.isCollapsingToShowActivityOverLockscreen()) {
+ if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
showBouncerIfKeyguard();
} else if (DEBUG) {
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
@@ -2522,7 +2536,7 @@
}
}
if (dismissShade) {
- if (mExpandedVisible) {
+ if (mExpandedVisible && !mBouncerShowing) {
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
true /* delayed*/);
} else {
@@ -2885,7 +2899,7 @@
mContext.unregisterReceiver(mDemoReceiver);
mAssistManager.destroy();
mHeadsUpManager.destroy();
- mStatusBarStateController.removeListener(this);
+ mStatusBarStateController.removeCallback(this);
if (mQSPanel != null && mQSPanel.getHost() != null) {
mQSPanel.getHost().destroy();
@@ -3203,6 +3217,7 @@
mNotificationPanel.onAffordanceLaunchEnded();
mNotificationPanel.animate().cancel();
mNotificationPanel.setAlpha(1f);
+ updateScrimController();
Trace.endSection();
return staying;
}
@@ -3931,6 +3946,10 @@
return;
}
+ // Set the state to pulsing, so ScrimController will know what to do once we ask it to
+ // execute the transition. The pulse callback will then be invoked when the scrims
+ // are black, indicating that StatusBar is ready to present the rest of the UI.
+ mPulsing = true;
mDozeScrimController.pulse(new PulseCallback() {
@Override
public void onPulseStarted() {
@@ -3944,6 +3963,7 @@
@Override
public void onPulseFinished() {
+ mPulsing = false;
callback.onPulseFinished();
setPulsing(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 7c17c01..4f25349 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -76,7 +76,7 @@
loadDimens();
SysUiServiceProvider.getComponent(context, CommandQueue.class)
- .addCallbacks(this);
+ .addCallback(this);
Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 484fe11..0f8970f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -151,7 +151,7 @@
mLockPatternUtils = lockPatternUtils;
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
public void registerStatusBar(StatusBar statusBar,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
new file mode 100644
index 0000000..c93d151
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER;
+import static com.android.systemui.SysUiServiceProvider.getComponent;
+import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.TaskStackBuilder;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.PreviewInflater;
+
+/**
+ * Status bar implementation of {@link NotificationActivityStarter}.
+ */
+public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
+
+ private static final String TAG = "NotificationClickHandler";
+
+ private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
+ private final NotificationGroupManager mGroupManager =
+ Dependency.get(NotificationGroupManager.class);
+ private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
+ (StatusBarRemoteInputCallback) Dependency.get(
+ NotificationRemoteInputManager.Callback.class);
+ private final NotificationRemoteInputManager mRemoteInputManager =
+ Dependency.get(NotificationRemoteInputManager.class);
+ private final NotificationLockscreenUserManager mLockscreenUserManager =
+ Dependency.get(NotificationLockscreenUserManager.class);
+ private final ShadeController mShadeController = Dependency.get(ShadeController.class);
+ private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+ private final NotificationEntryManager mEntryManager =
+ Dependency.get(NotificationEntryManager.class);
+ private final StatusBarStateController mStatusBarStateController =
+ Dependency.get(StatusBarStateController.class);
+
+ private final Context mContext;
+ private final NotificationPanelView mNotificationPanel;
+ private final NotificationPresenter mPresenter;
+ private final LockPatternUtils mLockPatternUtils;
+ private final HeadsUpManagerPhone mHeadsUpManager;
+ private final KeyguardManager mKeyguardManager;
+ private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final IStatusBarService mBarService;
+ private final CommandQueue mCommandQueue;
+
+ private boolean mIsCollapsingToShowActivityOverLockscreen;
+
+ public StatusBarNotificationActivityStarter(Context context,
+ NotificationPanelView panel,
+ NotificationPresenter presenter,
+ HeadsUpManagerPhone headsUpManager,
+ ActivityLaunchAnimator activityLaunchAnimator) {
+ mContext = context;
+ mNotificationPanel = panel;
+ mPresenter = presenter;
+ mLockPatternUtils = new LockPatternUtils(context);
+ mHeadsUpManager = headsUpManager;
+ mKeyguardManager = context.getSystemService(KeyguardManager.class);
+ mActivityLaunchAnimator = activityLaunchAnimator;
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mCommandQueue = getComponent(context, CommandQueue.class);
+ }
+
+ /**
+ * Called when a notification is clicked.
+ *
+ * @param sbn notification that was clicked
+ * @param row row for that notification
+ */
+ @Override
+ public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
+ RemoteInputController controller = mRemoteInputManager.getController();
+ if (controller.isRemoteInputActive(row.getEntry())
+ && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
+ // We have an active remote input typed and the user clicked on the notification.
+ // this was probably unintentional, so we're closing the edit text instead.
+ controller.closeRemoteInputs();
+ return;
+ }
+ Notification notification = sbn.getNotification();
+ final PendingIntent intent = notification.contentIntent != null
+ ? notification.contentIntent
+ : notification.fullScreenIntent;
+ final String notificationKey = sbn.getKey();
+
+ boolean isActivityIntent = intent.isActivity();
+ final boolean afterKeyguardGone = isActivityIntent
+ && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+ mLockscreenUserManager.getCurrentUserId());
+ final boolean wasOccluded = mShadeController.isOccluded();
+ boolean showOverLockscreen = mKeyguardMonitor.isShowing()
+ && PreviewInflater.wouldShowOverLockscreen(mContext,
+ intent.getIntent(),
+ mLockscreenUserManager.getCurrentUserId());
+ ActivityStarter.OnDismissAction postKeyguardAction =
+ () -> handleNotificationClickAfterKeyguardDismissed(
+ sbn, row, controller, intent, notificationKey,
+ isActivityIntent, wasOccluded, showOverLockscreen);
+ if (showOverLockscreen) {
+ mIsCollapsingToShowActivityOverLockscreen = true;
+ postKeyguardAction.onDismiss();
+ } else {
+ mActivityStarter.dismissKeyguardThenExecute(
+ postKeyguardAction, null /* cancel */, afterKeyguardGone);
+ }
+ }
+
+ private boolean handleNotificationClickAfterKeyguardDismissed(
+ StatusBarNotification sbn,
+ ExpandableNotificationRow row,
+ RemoteInputController controller,
+ PendingIntent intent,
+ String notificationKey,
+ boolean isActivityIntent,
+ boolean wasOccluded,
+ boolean showOverLockscreen) {
+ // TODO: Some of this code may be able to move to NotificationEntryManager.
+ if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
+ // Release the HUN notification to the shade.
+
+ if (mPresenter.isPresenterFullyCollapsed()) {
+ HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
+ }
+ //
+ // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+ // become canceled shortly by NoMan, but we can't assume that.
+ mHeadsUpManager.removeNotification(sbn.getKey(),
+ true /* releaseImmediately */);
+ }
+ StatusBarNotification parentToCancel = null;
+ if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
+ StatusBarNotification summarySbn =
+ mGroupManager.getLogicalGroupSummary(sbn).notification;
+ if (shouldAutoCancel(summarySbn)) {
+ parentToCancel = summarySbn;
+ }
+ }
+ final StatusBarNotification parentToCancelFinal = parentToCancel;
+ final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
+ sbn, row, controller, intent, notificationKey,
+ isActivityIntent, wasOccluded, parentToCancelFinal);
+
+ if (showOverLockscreen) {
+ mShadeController.addPostCollapseAction(runnable);
+ mShadeController.collapsePanel(true /* animate */);
+ } else if (mKeyguardMonitor.isShowing()
+ && mShadeController.isOccluded()) {
+ mShadeController.addAfterKeyguardGoneRunnable(runnable);
+ mShadeController.collapsePanel();
+ } else {
+ new Thread(runnable).start();
+ }
+
+ return !mNotificationPanel.isFullyCollapsed();
+ }
+
+ private void handleNotificationClickAfterPanelCollapsed(
+ StatusBarNotification sbn,
+ ExpandableNotificationRow row,
+ RemoteInputController controller,
+ PendingIntent intent,
+ String notificationKey,
+ boolean isActivityIntent,
+ boolean wasOccluded,
+ StatusBarNotification parentToCancelFinal) {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ int launchResult;
+ // If we are launching a work activity and require to launch
+ // separate work challenge, we defer the activity action and cancel
+ // notification until work challenge is unlocked.
+ if (isActivityIntent) {
+ final int userId = intent.getCreatorUserHandle().getIdentifier();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ // TODO(b/28935539): should allow certain activities to
+ // bypass work challenge
+ if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
+ intent.getIntentSender(), notificationKey)) {
+ // Show work challenge, do not run PendingIntent and
+ // remove notification
+ collapseOnMainThread();
+ return;
+ }
+ }
+ }
+ Intent fillInIntent = null;
+ NotificationData.Entry entry = row.getEntry();
+ CharSequence remoteInputText = null;
+ if (!TextUtils.isEmpty(entry.remoteInputText)) {
+ remoteInputText = entry.remoteInputText;
+ }
+ if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.key)) {
+ fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
+ remoteInputText.toString());
+ }
+ RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
+ row, wasOccluded);
+ try {
+ if (adapter != null) {
+ ActivityTaskManager.getService()
+ .registerRemoteAnimationForNextActivityStart(
+ intent.getCreatorPackage(), adapter);
+ }
+ launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
+ null, null, getActivityOptions(adapter));
+ mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
+ } catch (RemoteException | PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending contentIntent failed: " + e);
+
+ // TODO: Dismiss Keyguard.
+ }
+ if (isActivityIntent) {
+ mAssistManager.hideAssist();
+ }
+ if (shouldCollapse()) {
+ collapseOnMainThread();
+ }
+
+ final int count =
+ mEntryManager.getNotificationData().getActiveNotifications().size();
+ final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+ final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
+ rank, count, true);
+ try {
+ mBarService.onNotificationClick(notificationKey, nv);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ if (parentToCancelFinal != null) {
+ removeNotification(parentToCancelFinal);
+ }
+ if (shouldAutoCancel(sbn)
+ || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
+ notificationKey)) {
+ // Automatically remove all notifications that we may have kept around longer
+ removeNotification(sbn);
+ }
+ mIsCollapsingToShowActivityOverLockscreen = false;
+ }
+
+ @Override
+ public void startNotificationGutsIntent(final Intent intent, final int appUid,
+ ExpandableNotificationRow row) {
+ mActivityStarter.dismissKeyguardThenExecute(() -> {
+ AsyncTask.execute(() -> {
+ int launchResult = TaskStackBuilder.create(mContext)
+ .addNextIntentWithParentStack(intent)
+ .startActivities(getActivityOptions(
+ mActivityLaunchAnimator.getLaunchAnimation(
+ row, mShadeController.isOccluded())),
+ new UserHandle(UserHandle.getUserId(appUid)));
+ mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
+ if (shouldCollapse()) {
+ // Putting it back on the main thread, since we're touching views
+ Dependency.get(MAIN_HANDLER).post(() -> mCommandQueue.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
+ }
+ });
+ return true;
+ }, null, false /* afterKeyguardGone */);
+ }
+
+ @Override
+ public boolean isCollapsingToShowActivityOverLockscreen() {
+ return mIsCollapsingToShowActivityOverLockscreen;
+ }
+
+ private static boolean shouldAutoCancel(StatusBarNotification sbn) {
+ int flags = sbn.getNotification().flags;
+ if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
+ return false;
+ }
+ if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ private void collapseOnMainThread() {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ mShadeController.collapsePanel();
+ } else {
+ Dependency.get(MAIN_HANDLER).post(mShadeController::collapsePanel);
+ }
+ }
+
+ private boolean shouldCollapse() {
+ return mStatusBarStateController.getState() != StatusBarState.SHADE
+ || !mActivityLaunchAnimator.isAnimationPending();
+ }
+
+ private void removeNotification(StatusBarNotification notification) {
+ // We have to post it to the UI thread for synchronization
+ Dependency.get(MAIN_HANDLER).post(() -> {
+ Runnable removeRunnable =
+ () -> mEntryManager.performRemoveNotification(notification);
+ if (mPresenter.isCollapsing()) {
+ // To avoid lags we're only performing the remove
+ // after the shade was collapsed
+ mShadeController.addPostCollapseAction(removeRunnable);
+ } else {
+ removeRunnable.run();
+ }
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 588c3a8..acdd5e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -14,35 +14,22 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Dependency.MAIN_HANDLER;
import static com.android.systemui.SysUiServiceProvider.getComponent;
import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
-import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.TaskStackBuilder;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import android.view.RemoteAnimationAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
@@ -50,13 +37,12 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.MessagingGroup;
+import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.R;
-import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.AmbientPulseManager;
@@ -65,9 +51,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
@@ -80,11 +64,11 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.PreviewInflater;
-public class StatusBarNotificationPresenter implements NotificationPresenter {
+public class StatusBarNotificationPresenter implements NotificationPresenter,
+ ConfigurationController.ConfigurationListener {
private final LockscreenGestureLogger mLockscreenGestureLogger =
Dependency.get(LockscreenGestureLogger.class);
@@ -93,7 +77,6 @@
private final ShadeController mShadeController = Dependency.get(ShadeController.class);
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
- private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
private final NotificationViewHierarchyManager mViewHierarchyManager =
Dependency.get(NotificationViewHierarchyManager.class);
@@ -105,12 +88,6 @@
Dependency.get(NotificationEntryManager.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
- private final NotificationRemoteInputManager mRemoteInputManager =
- Dependency.get(NotificationRemoteInputManager.class);
- private final NotificationGroupManager mGroupManager =
- Dependency.get(NotificationGroupManager.class);
- private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
- (StatusBarRemoteInputCallback) Dependency.get(Callback.class);
protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
private final NotificationPanelView mNotificationPanel;
@@ -122,9 +99,9 @@
private final CommandQueue mCommandQueue;
private final AccessibilityManager mAccessibilityManager;
- private final LockPatternUtils mLockPatternUtils;
private final KeyguardManager mKeyguardManager;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final int mMaxAllowedKeyguardNotifications;
private final IStatusBarService mBarService;
private boolean mReinflateNotificationsOnUserSwitched;
@@ -133,34 +110,34 @@
protected boolean mVrMode;
private int mMaxKeyguardNotifications;
- private boolean mIsCollapsingToShowActivityOverLockscreen;
- public StatusBarNotificationPresenter(Context context, NotificationPanelView panel,
- HeadsUpManagerPhone headsUp, StatusBarWindowView statusBarWindow,
- ViewGroup stackScroller, DozeScrimController dozeScrimController,
+ public StatusBarNotificationPresenter(Context context,
+ NotificationPanelView panel,
+ HeadsUpManagerPhone headsUp,
+ StatusBarWindowView statusBarWindow,
+ ViewGroup stackScroller,
+ DozeScrimController dozeScrimController,
ScrimController scrimController,
- ActivityLaunchAnimator.Callback launchAnimatorCallback) {
+ ActivityLaunchAnimator activityLaunchAnimator,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
mContext = context;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
mCommandQueue = getComponent(context, CommandQueue.class);
mAboveShelfObserver = new AboveShelfObserver(stackScroller);
+ mActivityLaunchAnimator = activityLaunchAnimator;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mAboveShelfObserver.setListener(statusBarWindow.findViewById(
R.id.notification_container_parent));
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mDozeScrimController = dozeScrimController;
mScrimController = scrimController;
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
- mLockPatternUtils = new LockPatternUtils(context);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mMaxAllowedKeyguardNotifications = context.getResources().getInteger(
R.integer.keyguard_max_notification_count);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mActivityLaunchAnimator = new ActivityLaunchAnimator(statusBarWindow,
- launchAnimatorCallback,
- mNotificationPanel,
- (NotificationListContainer) stackScroller);
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
@@ -176,10 +153,12 @@
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
}
- mRemoteInputManager.setUpWithPresenter(this,
+ NotificationRemoteInputManager remoteInputManager =
+ Dependency.get(NotificationRemoteInputManager.class);
+ remoteInputManager.setUpWithCallback(
Dependency.get(NotificationRemoteInputManager.Callback.class),
mNotificationPanel.createRemoteInputDelegate());
- mRemoteInputManager.getController().addCallback(
+ remoteInputManager.getController().addCallback(
Dependency.get(StatusBarWindowController.class));
NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
@@ -193,9 +172,13 @@
onUserSwitched(mLockscreenUserManager.getCurrentUserId());
});
+ Dependency.get(ConfigurationController.class).addCallback(this);
}
+ @Override
public void onDensityOrFontScaleChanged() {
+ MessagingMessage.dropCache();
+ MessagingGroup.dropCache();
if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
} else {
@@ -204,11 +187,6 @@
}
@Override
- public ActivityLaunchAnimator getActivityLaunchAnimator() {
- return mActivityLaunchAnimator;
- }
-
- @Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
|| mActivityLaunchAnimator.isAnimationPending()
@@ -216,11 +194,6 @@
}
@Override
- public boolean isCollapsingToShowActivityOverLockscreen() {
- return mIsCollapsingToShowActivityOverLockscreen;
- }
-
- @Override
public void onPerformRemoveNotification(StatusBarNotification n) {
if (mNotificationPanel.hasPulsingNotifications() &&
!mAmbientPulseManager.hasNotifications()) {
@@ -377,201 +350,6 @@
}
@Override
- public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
- RemoteInputController controller = mRemoteInputManager.getController();
- if (controller.isRemoteInputActive(row.getEntry())
- && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
- // We have an active remote input typed and the user clicked on the notification.
- // this was probably unintentional, so we're closing the edit text instead.
- controller.closeRemoteInputs();
- return;
- }
- Notification notification = sbn.getNotification();
- final PendingIntent intent = notification.contentIntent != null
- ? notification.contentIntent
- : notification.fullScreenIntent;
- final String notificationKey = sbn.getKey();
-
- boolean isActivityIntent = intent.isActivity();
- final boolean afterKeyguardGone = isActivityIntent
- && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
- mLockscreenUserManager.getCurrentUserId());
- final boolean wasOccluded = mShadeController.isOccluded();
- boolean showOverLockscreen = mKeyguardMonitor.isShowing()
- && PreviewInflater.wouldShowOverLockscreen(mContext,
- intent.getIntent(),
- mLockscreenUserManager.getCurrentUserId());
- OnDismissAction postKeyguardAction = () -> {
- // TODO: Some of this code may be able to move to NotificationEntryManager.
- if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
- // Release the HUN notification to the shade.
-
- if (isPresenterFullyCollapsed()) {
- HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
- }
- //
- // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
- // become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpManager.removeNotification(sbn.getKey(),
- true /* releaseImmediately */);
- }
- StatusBarNotification parentToCancel = null;
- if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
- StatusBarNotification summarySbn =
- mGroupManager.getLogicalGroupSummary(sbn).notification;
- if (shouldAutoCancel(summarySbn)) {
- parentToCancel = summarySbn;
- }
- }
- final StatusBarNotification parentToCancelFinal = parentToCancel;
- final Runnable runnable = () -> {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- int launchResult = ActivityManager.START_CANCELED;
- if (intent != null) {
- // If we are launching a work activity and require to launch
- // separate work challenge, we defer the activity action and cancel
- // notification until work challenge is unlocked.
- if (isActivityIntent) {
- final int userId = intent.getCreatorUserHandle().getIdentifier();
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
- && mKeyguardManager.isDeviceLocked(userId)) {
- // TODO(b/28935539): should allow certain activities to
- // bypass work challenge
- if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
- intent.getIntentSender(), notificationKey)) {
- // Show work challenge, do not run PendingIntent and
- // remove notification
- collapseOnMainThread();
- return;
- }
- }
- }
- Intent fillInIntent = null;
- Entry entry = row.getEntry();
- CharSequence remoteInputText = null;
- if (!TextUtils.isEmpty(entry.remoteInputText)) {
- remoteInputText = entry.remoteInputText;
- }
- if (!TextUtils.isEmpty(remoteInputText)
- && !controller.isSpinning(entry.key)) {
- fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
- remoteInputText.toString());
- }
- RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
- row, wasOccluded);
- try {
- if (adapter != null) {
- ActivityTaskManager.getService()
- .registerRemoteAnimationForNextActivityStart(
- intent.getCreatorPackage(), adapter);
- }
- launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
- null, null, getActivityOptions(adapter));
- mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
- } catch (RemoteException | PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending contentIntent failed: " + e);
-
- // TODO: Dismiss Keyguard.
- }
- if (isActivityIntent) {
- mAssistManager.hideAssist();
- }
- }
- if (shouldCollapse()) {
- collapseOnMainThread();
- }
-
- final int count =
- mEntryManager.getNotificationData().getActiveNotifications().size();
- final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
- final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
- rank, count, true);
- try {
- mBarService.onNotificationClick(notificationKey, nv);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- if (parentToCancelFinal != null) {
- removeNotification(parentToCancelFinal);
- }
- if (shouldAutoCancel(sbn)
- || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
- notificationKey)) {
- // Automatically remove all notifications that we may have kept around longer
- removeNotification(sbn);
- }
- mIsCollapsingToShowActivityOverLockscreen = false;
- };
-
- if (showOverLockscreen) {
- mShadeController.addPostCollapseAction(runnable);
- mShadeController.collapsePanel(true /* animate */);
- } else if (mKeyguardMonitor.isShowing()
- && mShadeController.isOccluded()) {
- mShadeController.addAfterKeyguardGoneRunnable(runnable);
- mShadeController.collapsePanel();
- } else {
- new Thread(runnable).start();
- }
-
- return !mNotificationPanel.isFullyCollapsed();
- };
- if (showOverLockscreen) {
- mIsCollapsingToShowActivityOverLockscreen = true;
- postKeyguardAction.onDismiss();
- } else {
- mActivityStarter.dismissKeyguardThenExecute(
- postKeyguardAction, null /* cancel */, afterKeyguardGone);
- }
- }
-
- private void removeNotification(StatusBarNotification notification) {
- // We have to post it to the UI thread for synchronization
- Dependency.get(MAIN_HANDLER).post(() -> {
- Runnable removeRunnable =
- () -> mEntryManager.performRemoveNotification(notification);
- if (isCollapsing()) {
- // To avoid lags we're only performing the remove
- // after the shade was collapsed
- mShadeController.addPostCollapseAction(removeRunnable);
- } else {
- removeRunnable.run();
- }
- });
- }
-
- @Override
- public void startNotificationGutsIntent(final Intent intent, final int appUid,
- ExpandableNotificationRow row) {
- mActivityStarter.dismissKeyguardThenExecute(() -> {
- AsyncTask.execute(() -> {
- int launchResult = TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(
- mActivityLaunchAnimator.getLaunchAnimation(
- row, mShadeController.isOccluded())),
- new UserHandle(UserHandle.getUserId(appUid)));
- mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
- if (shouldCollapse()) {
- // Putting it back on the main thread, since we're touching views
- Dependency.get(MAIN_HANDLER).post(() -> mCommandQueue.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
- }
- });
- return true;
- }, null, false /* afterKeyguardGone */);
- }
-
- @Override
public int getMaxNotificationsWhileLocked(boolean recompute) {
if (recompute) {
mMaxKeyguardNotifications = Math.max(1,
@@ -602,20 +380,8 @@
@Override
public boolean isPresenterLocked() {
- return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
- }
-
- private void collapseOnMainThread() {
- if (Looper.getMainLooper().isCurrentThread()) {
- mShadeController.collapsePanel();
- } else {
- Dependency.get(MAIN_HANDLER).post(mShadeController::collapsePanel);
- }
- }
-
- private boolean shouldCollapse() {
- return mStatusBarStateController.getState() != StatusBarState.SHADE
- || !mActivityLaunchAnimator.isAnimationPending();
+ return mStatusBarKeyguardViewManager.isShowing()
+ && mStatusBarKeyguardViewManager.isSecure();
}
private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
@@ -624,17 +390,6 @@
true /* afterKeyguardGone */);
}
- private static boolean shouldAutoCancel(StatusBarNotification sbn) {
- int flags = sbn.getNotification().flags;
- if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
- return false;
- }
- if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return false;
- }
- return true;
- }
-
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
@@ -645,7 +400,6 @@
private final CheckSaveListener mCheckSaveListener = new CheckSaveListener() {
@Override
public void checkSave(Runnable saveImportance, StatusBarNotification sbn) {
- int state = mStatusBarStateController.getState();
// If the user has security enabled, show challenge if the setting is changed.
if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier())
&& mKeyguardManager.isKeyguardLocked()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index a743d41e..78f5374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -17,8 +17,7 @@
import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
import static com.android.systemui.SysUiServiceProvider.getComponent;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager
- .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -70,10 +69,10 @@
mContext = context;
mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
- mStatusBarStateController.addListener(mStateListener);
+ mStatusBarStateController.addCallback(mStateListener);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mCommandQueue = getComponent(context, CommandQueue.class);
- mCommandQueue.addCallbacks(this);
+ mCommandQueue.addCallback(this);
}
private void setStatusBarState(int state) {
@@ -163,7 +162,7 @@
mPendingWorkRemoteInputView = clicked;
}
- protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
+ boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
String notificationKey) {
// Clear pending remote view, as we do not want to trigger pending remote input view when
// it's called by other code
@@ -206,7 +205,7 @@
}
@Override
- public boolean handleRemoteViewClick(PendingIntent pendingIntent,
+ public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
NotificationRemoteInputManager.ClickHandler defaultHandler) {
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 158ee8a..cb6e300 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -84,7 +84,7 @@
}
@VisibleForTesting
- StatusBarWindowController(Context context, WindowManager windowManager,
+ public StatusBarWindowController(Context context, WindowManager windowManager,
IActivityManager activityManager, DozeParameters dozeParameters) {
mContext = context;
mWindowManager = windowManager;
@@ -92,7 +92,7 @@
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
- Dependency.get(StatusBarStateController.class).addListener(
+ Dependency.get(StatusBarStateController.class).addCallback(
mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
index 9042ca6..626eef5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
@@ -1,8 +1,9 @@
/*
* 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
@@ -14,7 +15,35 @@
package com.android.systemui.statusbar.policy;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.Lifecycle.Event;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
public interface CallbackController<T> {
void addCallback(T listener);
void removeCallback(T listener);
+
+ /**
+ * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state
+ * and {@link #removeCallback(Object)} when not resumed automatically.
+ */
+ default T observe(LifecycleOwner owner, T listener) {
+ return observe(owner.getLifecycle(), listener);
+ }
+
+ /**
+ * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state
+ * and {@link #removeCallback(Object)} when not resumed automatically.
+ */
+ default T observe(Lifecycle lifecycle, T listener) {
+ lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> {
+ if (event == Event.ON_RESUME) {
+ addCallback(listener);
+ } else if (event == Event.ON_PAUSE) {
+ removeCallback(listener);
+ }
+ });
+ return listener;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 8517d90..aafdcd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -165,7 +165,7 @@
mClockVisibleByUser = bundle.getBoolean(VISIBLE_BY_USER, true);
mShowSeconds = bundle.getBoolean(SHOW_SECONDS, false);
if (bundle.containsKey(VISIBILITY)) {
- setVisibility(bundle.getInt(VISIBILITY));
+ super.setVisibility(bundle.getInt(VISIBILITY));
}
}
@@ -187,7 +187,7 @@
null, Dependency.get(Dependency.TIME_TICK_HANDLER));
Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
StatusBarIconController.ICON_BLACKLIST);
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
if (mShowDark) {
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
}
@@ -203,6 +203,7 @@
// Make sure we update to the current time
updateClock();
+ updateClockVisibility();
updateShowSeconds();
}
@@ -214,7 +215,7 @@
mAttached = false;
Dependency.get(TunerService.class).removeTunable(this);
SysUiServiceProvider.getComponent(getContext(), CommandQueue.class)
- .removeCallbacks(this);
+ .removeCallback(this);
if (mShowDark) {
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
}
@@ -247,6 +248,15 @@
}
};
+ @Override
+ public void setVisibility(int visibility) {
+ if (visibility == View.VISIBLE && !shouldBeVisible()) {
+ return;
+ }
+
+ super.setVisibility(visibility);
+ }
+
public void setClockVisibleByUser(boolean visible) {
mClockVisibleByUser = visible;
updateClockVisibility();
@@ -257,11 +267,15 @@
updateClockVisibility();
}
+ private boolean shouldBeVisible() {
+ return mClockVisibleByPolicy && mClockVisibleByUser;
+ }
+
private void updateClockVisibility() {
- boolean visible = mClockVisibleByPolicy && mClockVisibleByUser;
+ boolean visible = shouldBeVisible();
Dependency.get(IconLogger.class).onIconVisibility("clock", visible);
int visibility = visible ? View.VISIBLE : View.GONE;
- setVisibility(visibility);
+ super.setVisibility(visibility);
}
final void updateClock() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
index 639e50c..9c099f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.policy;
-import android.os.SystemProperties;
+import android.sysprop.VoldProperties;
/**
* Helper for determining whether the phone is decrypted yet.
@@ -26,7 +26,7 @@
public static final boolean IS_DATA_ENCRYPTED = isDataEncrypted();
private static boolean isDataEncrypted() {
- String voldState = SystemProperties.get("vold.decrypt");
+ String voldState = VoldProperties.decrypt().orElse("");
return "1".equals(voldState) || "trigger_restart_min_framework".equals(voldState);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index f36066c..4fa8321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -16,12 +16,13 @@
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.text.Layout;
import android.text.TextPaint;
import android.text.method.TransformationMethod;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Size;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -35,12 +36,14 @@
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.text.BreakIterator;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
@@ -61,6 +64,8 @@
private final SmartReplyConstants mConstants;
private final KeyguardDismissUtil mKeyguardDismissUtil;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
/**
* The upper bound for the height of this view in pixels. Notifications are automatically
@@ -109,6 +114,7 @@
super(context, attrs);
mConstants = Dependency.get(SmartReplyConstants.class);
mKeyguardDismissUtil = Dependency.get(KeyguardDismissUtil.class);
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mHeightUpperLimit = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.smart_reply_button_max_height);
@@ -209,13 +215,15 @@
* notification are shown.
*/
public void addSmartActions(SmartActions smartActions,
- SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ SmartReplyController smartReplyController, NotificationData.Entry entry,
+ HeadsUpManager headsUpManager) {
int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
Notification.Action action = smartActions.actions.get(n);
if (action.actionIntent != null) {
Button actionButton = inflateActionButton(
- getContext(), this, n, smartActions, smartReplyController, entry);
+ getContext(), this, n, smartActions, smartReplyController, entry,
+ headsUpManager);
addView(actionButton);
}
}
@@ -237,12 +245,22 @@
b.setText(choice);
OnDismissAction action = () -> {
+ // TODO(b/111437455): Also for EDIT_CHOICES_BEFORE_SENDING_AUTO, depending on flags.
+ if (smartReplies.remoteInput.getEditChoicesBeforeSending()
+ == RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED) {
+ entry.remoteInputText = choice;
+ mRemoteInputManager.activateRemoteInput(b,
+ new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput,
+ smartReplies.pendingIntent);
+ return false;
+ }
+
smartReplyController.smartReplySent(
entry, replyIndex, b.getText(), smartReplies.fromAssistant);
Bundle results = new Bundle();
results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(new RemoteInput[]{smartReplies.remoteInput}, intent,
+ RemoteInput.addResultsToIntent(new RemoteInput[] { smartReplies.remoteInput }, intent,
results);
RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
entry.setHasSentReply();
@@ -274,7 +292,7 @@
@VisibleForTesting
Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
SmartActions smartActions, SmartReplyController smartReplyController,
- NotificationData.Entry entry) {
+ NotificationData.Entry entry, HeadsUpManager headsUpManager) {
Notification.Action action = smartActions.actions.get(actionIndex);
Button button = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_action_button, root, false);
@@ -282,41 +300,31 @@
Drawable iconDrawable = action.getIcon().loadDrawable(context);
// Add the action icon to the Smart Action button.
- Size newIconSize = calculateIconSizeFromSingleLineButton(context, root,
- new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight()));
- iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight());
+ int newIconSize = context.getResources().getDimensionPixelSize(
+ R.dimen.smart_action_button_icon_size);
+ iconDrawable.setBounds(0, 0, newIconSize, newIconSize);
button.setCompoundDrawables(iconDrawable, null, null, null);
button.setOnClickListener(view ->
getActivityStarter().startPendingIntentDismissingKeyguard(
action.actionIntent,
- () -> smartReplyController.smartActionClicked(
- entry, actionIndex, action, smartActions.fromAssistant)));
+ () -> {
+ smartReplyController.smartActionClicked(
+ entry, actionIndex, action, smartActions.fromAssistant);
+ postOnUiThread(() ->
+ headsUpManager.removeNotification(entry.key, true));
+ }));
// TODO(b/119010281): handle accessibility
+ // Mark this as an Action button
+ final LayoutParams lp = (LayoutParams) button.getLayoutParams();
+ lp.buttonType = SmartButtonType.ACTION;
return button;
}
- private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root,
- Size originalIconSize) {
- Button button = (Button) LayoutInflater.from(context).inflate(
- R.layout.smart_action_button, root, false);
- // Add simple text here to ensure the button displays one line of text.
- button.setText("a");
- return calculateIconSizeFromButtonHeight(button, originalIconSize);
- }
-
- // Given a button with text on a single line - we want to add an icon to that button. This
- // method calculates the icon height to use to avoid making the button grow in height.
- private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) {
- // A completely permissive measure spec should make the button text single-line.
- button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH);
- int buttonHeight = button.getMeasuredHeight();
- int newIconHeight = buttonHeight / 2;
- int newIconWidth = (int) (originalIconSize.getWidth()
- * ((double) newIconHeight) / originalIconSize.getHeight());
- return new Size(newIconWidth, newIconHeight);
+ private void postOnUiThread(Runnable runnable) {
+ mMainThreadHandler.post(runnable);
}
@Override
@@ -352,18 +360,26 @@
int displayedChildCount = 0;
int buttonPaddingHorizontal = mSingleLineButtonPaddingHorizontal;
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
+ // Set up a list of suggestions where actions come before replies. Note that the Buttons
+ // themselves have already been added to the view hierarchy in an order such that Smart
+ // Replies are shown before Smart Actions. The order of the list below determines which
+ // suggestions will be shown at all - only the first X elements are shown (where X depends
+ // on how much space each suggestion button needs).
+ List<View> smartActions = filterActionsOrReplies(SmartButtonType.ACTION);
+ List<View> smartReplies = filterActionsOrReplies(SmartButtonType.REPLY);
+ List<View> smartSuggestions = new ArrayList<>(smartActions);
+ smartSuggestions.addAll(smartReplies);
+ List<View> coveredSuggestions = new ArrayList<>();
+
+ for (View child : smartSuggestions) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
- continue;
- }
child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
buttonPaddingHorizontal, child.getPaddingBottom());
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
+ coveredSuggestions.add(child);
+
final int lineCount = ((Button) child).getLineCount();
if (lineCount < 1 || lineCount > 2) {
// If smart reply has no text, or more than two lines, then don't show it.
@@ -417,7 +433,8 @@
// Mark all buttons from the last squeezing round as "failed to squeeze", so
// that they're re-measured without squeezing later.
- markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_FAILED, i);
+ markButtonsWithPendingSqueezeStatusAs(
+ LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);
// The current button doesn't fit, so there's no point in measuring further
// buttons.
@@ -426,7 +443,8 @@
// The current button fits, so mark all squeezed buttons as "successfully squeezed"
// to prevent them from being un-squeezed in a subsequent squeezing round.
- markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, i);
+ markButtonsWithPendingSqueezeStatusAs(
+ LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, coveredSuggestions);
}
lp.show = true;
@@ -445,6 +463,22 @@
mPaddingTop + maxChildHeight + mPaddingBottom), heightMeasureSpec));
}
+ private List<View> filterActionsOrReplies(SmartButtonType buttonType) {
+ List<View> actions = new ArrayList<>();
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
+ continue;
+ }
+ if (lp.buttonType == buttonType) {
+ actions.add(child);
+ }
+ }
+ return actions;
+ }
+
private void resetButtonsLayoutParams() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -615,9 +649,9 @@
}
}
- private void markButtonsWithPendingSqueezeStatusAs(int squeezeStatus, int maxChildIndex) {
- for (int i = 0; i <= maxChildIndex; i++) {
- final View child = getChildAt(i);
+ private void markButtonsWithPendingSqueezeStatusAs(
+ int squeezeStatus, List<View> coveredChildren) {
+ for (View child : coveredChildren) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.squeezeStatus == LayoutParams.SQUEEZE_STATUS_PENDING) {
lp.squeezeStatus = squeezeStatus;
@@ -712,6 +746,11 @@
return mActivityStarter;
}
+ private enum SmartButtonType {
+ REPLY,
+ ACTION
+ }
+
@VisibleForTesting
static class LayoutParams extends ViewGroup.LayoutParams {
@@ -737,6 +776,7 @@
private boolean show = false;
private int squeezeStatus = SQUEEZE_STATUS_NONE;
+ private SmartButtonType buttonType = SmartButtonType.REPLY;
private LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index cd379c5..4a69cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -42,7 +42,7 @@
public void start() {
putComponent(TvStatusBar.class, this);
CommandQueue commandQueue = getComponent(CommandQueue.class);
- commandQueue.addCallbacks(this);
+ commandQueue.addCallback(this);
int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<>();
ArrayList<String> iconSlots = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java
index 0f7c9a4..096ac3f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java
@@ -18,19 +18,24 @@
import android.os.Looper;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Helper providing common assertions.
*/
public class Assert {
+ @VisibleForTesting
+ public static Looper sMainLooper = Looper.getMainLooper();
+
public static void isMainThread() {
- if (!Looper.getMainLooper().isCurrentThread()) {
+ if (!sMainLooper.isCurrentThread()) {
throw new IllegalStateException("should be called from the main thread.");
}
}
public static void isNotMainThread() {
- if (Looper.getMainLooper().isCurrentThread()) {
+ if (sMainLooper.isCurrentThread()) {
throw new IllegalStateException("should not be called from the main thread.");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java b/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java
new file mode 100644
index 0000000..c7f385d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
+import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.annotation.CallSuper;
+import android.app.Fragment;
+import android.os.Bundle;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+/**
+ * Version of {@link #Fragment} that is a {@link LifecycleOwner}.
+ */
+public class LifecycleFragment extends Fragment implements LifecycleOwner {
+
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
+
+ @CallSuper
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
+ super.onCreate(savedInstanceState);
+ }
+
+ @CallSuper
+ @Override
+ public void onStart() {
+ mLifecycle.handleLifecycleEvent(ON_START);
+ super.onStart();
+ }
+
+ @CallSuper
+ @Override
+ public void onResume() {
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
+ super.onResume();
+ }
+
+ @CallSuper
+ @Override
+ public void onPause() {
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
+ super.onPause();
+ }
+
+ @CallSuper
+ @Override
+ public void onStop() {
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+ super.onStop();
+ }
+
+ @CallSuper
+ @Override
+ public void onDestroy() {
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
+ super.onDestroy();
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
new file mode 100644
index 0000000..711a0df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.State.DESTROYED;
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+/**
+ * Tools for generating lifecycle from sysui objects.
+ */
+public class SysuiLifecycle {
+
+ private SysuiLifecycle() {
+ }
+
+ /**
+ * Get a lifecycle that will be put into the resumed state when the view is attached
+ * and goes to the destroyed state when the view is detached.
+ */
+ public static LifecycleOwner viewAttachLifecycle(View v) {
+ return new ViewLifecycle(v);
+ }
+
+ private static class ViewLifecycle implements LifecycleOwner, OnAttachStateChangeListener {
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+ ViewLifecycle(View v) {
+ v.addOnAttachStateChangeListener(this);
+ }
+
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mLifecycle.markState(RESUMED);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ mLifecycle.markState(DESTROYED);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 6812410..490cdd5c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -57,13 +57,13 @@
public void onViewAttachedToWindow(View v) {
mView = v;
SysUiServiceProvider.getComponent(v.getContext(), CommandQueue.class)
- .addCallbacks(this);
+ .addCallback(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
SysUiServiceProvider.getComponent(mView.getContext(), CommandQueue.class)
- .removeCallbacks(this);
+ .removeCallback(this);
mView = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
deleted file mode 100644
index 9b616e0..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
-import android.app.Dialog;
-import android.app.KeyguardManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.media.CarAudioManager;
-import android.car.media.ICarVolumeCallback;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.ServiceConnection;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-
-import androidx.car.widget.ListItem;
-import androidx.car.widget.ListItemAdapter;
-import androidx.car.widget.ListItemAdapter.BackgroundStyle;
-import androidx.car.widget.ListItemProvider.ListProvider;
-import androidx.car.widget.PagedListView;
-import androidx.car.widget.SeekbarListItem;
-
-import com.android.systemui.R;
-import com.android.systemui.plugins.VolumeDialog;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Car version of the volume dialog.
- *
- * Methods ending in "H" must be called on the (ui) handler.
- */
-public class CarVolumeDialogImpl implements VolumeDialog {
- private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
-
- private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
- private static final String XML_TAG_VOLUME_ITEM = "item";
- private static final int HOVERING_TIMEOUT = 16000;
- private static final int NORMAL_TIMEOUT = 3000;
- private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
- private static final int DISMISS_DELAY_IN_MILLIS = 50;
- private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
-
- private final Context mContext;
- private final H mHandler = new H();
-
- private Window mWindow;
- private CustomDialog mDialog;
- private PagedListView mListView;
- private ListItemAdapter mPagedListAdapter;
- // All the volume items.
- private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
- // Available volume items in car audio manager.
- private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
- // Volume items in the PagedListView.
- private final List<ListItem> mVolumeLineItems = new ArrayList<>();
- private final KeyguardManager mKeyguard;
-
- private Car mCar;
- private CarAudioManager mCarAudioManager;
-
- private boolean mHovering;
- private boolean mShowing;
- private boolean mExpanded;
-
- public CarVolumeDialogImpl(Context context) {
- mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
- mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mCar = Car.createCar(mContext, mServiceConnection);
- }
-
- public void init(int windowType, Callback callback) {
- initDialog();
-
- mCar.connect();
- }
-
- @Override
- public void destroy() {
- mHandler.removeCallbacksAndMessages(null);
-
- cleanupAudioManager();
- // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
- // audio manager beforehand.
- mCar.disconnect();
- }
-
- private void initDialog() {
- loadAudioUsageItems();
- mVolumeLineItems.clear();
- mDialog = new CustomDialog(mContext);
-
- mHovering = false;
- mShowing = false;
- mExpanded = false;
- mWindow = mDialog.getWindow();
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
- mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
- final WindowManager.LayoutParams lp = mWindow.getAttributes();
- lp.format = PixelFormat.TRANSLUCENT;
- lp.setTitle(VolumeDialogImpl.class.getSimpleName());
- lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.windowAnimations = -1;
- mWindow.setAttributes(lp);
- mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- mDialog.setCanceledOnTouchOutside(true);
- mDialog.setContentView(R.layout.car_volume_dialog);
- mDialog.setOnShowListener(dialog -> {
- mListView.setTranslationY(-mListView.getHeight());
- mListView.setAlpha(0);
- mListView.animate()
- .alpha(1)
- .translationY(0)
- .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
- .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
- .start();
- });
- mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
- mListView.setOnHoverListener((v, event) -> {
- int action = event.getActionMasked();
- mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
- || (action == MotionEvent.ACTION_HOVER_MOVE);
- rescheduleTimeoutH();
- return true;
- });
-
- mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
- BackgroundStyle.PANEL);
- mListView.setAdapter(mPagedListAdapter);
- mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
- }
-
- public void show(int reason) {
- mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
- }
-
- public void dismiss(int reason) {
- mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
- }
-
- private void showH(int reason) {
- if (D.BUG) {
- Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
- }
-
- mHandler.removeMessages(H.SHOW);
- mHandler.removeMessages(H.DISMISS);
- rescheduleTimeoutH();
- // Refresh the data set before showing.
- mPagedListAdapter.notifyDataSetChanged();
- if (mShowing) {
- return;
- }
- mShowing = true;
-
- mDialog.show();
- Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
- }
-
- protected void rescheduleTimeoutH() {
- mHandler.removeMessages(H.DISMISS);
- final int timeout = computeTimeoutH();
- mHandler.sendMessageDelayed(mHandler
- .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
-
- if (D.BUG) {
- Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
- }
- }
-
- private int computeTimeoutH() {
- return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
- }
-
- protected void dismissH(int reason) {
- if (D.BUG) {
- Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
- }
-
- mHandler.removeMessages(H.DISMISS);
- mHandler.removeMessages(H.SHOW);
- if (!mShowing) {
- return;
- }
-
- mListView.animate().cancel();
-
- mListView.setTranslationY(0);
- mListView.setAlpha(1);
- mListView.animate()
- .alpha(0)
- .translationY(-mListView.getHeight())
- .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
- .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
- .withEndAction(() -> mHandler.postDelayed(() -> {
- if (D.BUG) {
- Log.d(TAG, "mDialog.dismiss()");
- }
- mDialog.dismiss();
- mShowing = false;
- }, DISMISS_DELAY_IN_MILLIS))
- .start();
-
- Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
- }
-
- public void dump(PrintWriter writer) {
- writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
- writer.print(" mShowing: "); writer.println(mShowing);
- }
-
- private void loadAudioUsageItems() {
- try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
- AttributeSet attrs = Xml.asAttributeSet(parser);
- int type;
- // Traverse to the first start tag
- while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
- && type != XmlResourceParser.START_TAG) {
- }
-
- if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
- throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
- }
- int outerDepth = parser.getDepth();
- int rank = 0;
- while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
- && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlResourceParser.END_TAG) {
- continue;
- }
- if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
- TypedArray item = mContext.getResources().obtainAttributes(
- attrs, R.styleable.carVolumeItems_item);
- int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
- if (usage >= 0) {
- VolumeItem volumeItem = new VolumeItem();
- volumeItem.usage = usage;
- volumeItem.rank = rank;
- volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0);
- mVolumeItems.put(usage, volumeItem);
- rank++;
- }
- item.recycle();
- }
- }
- } catch (XmlPullParserException | IOException e) {
- Log.e(TAG, "Error parsing volume groups configuration", e);
- }
- }
-
- private VolumeItem getVolumeItemForUsages(int[] usages) {
- int rank = Integer.MAX_VALUE;
- VolumeItem result = null;
- for (int usage : usages) {
- VolumeItem volumeItem = mVolumeItems.get(usage);
- if (volumeItem.rank < rank) {
- rank = volumeItem.rank;
- result = volumeItem;
- }
- }
- return result;
- }
-
- private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
- }
-
- private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupMaxVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
- }
-
- private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId,
- int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) {
- SeekbarListItem listItem = new SeekbarListItem(mContext);
- listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
- int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
- int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
- listItem.setProgress(progress);
- listItem.setOnSeekBarChangeListener(
- new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
- Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
- primaryIcon.mutate().setTint(color);
- listItem.setPrimaryActionIcon(primaryIcon);
- if (supplementalIconId != 0) {
- Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
- supplementalIcon.mutate().setTint(color);
- listItem.setSupplementalIcon(supplementalIcon, true);
- listItem.setSupplementalIconListener(supplementalIconOnClickListener);
- } else {
- listItem.setSupplementalEmptyIcon(true);
- listItem.setSupplementalIconListener(null);
- }
-
- mVolumeLineItems.add(listItem);
- volumeItem.listItem = listItem;
- volumeItem.progress = progress;
- return listItem;
- }
-
- private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
- for (int i = 0; i < mVolumeItems.size(); ++i) {
- VolumeItem volumeItem = mVolumeItems.valueAt(i);
- if (volumeItem.listItem == targetItem) {
- return volumeItem;
- }
- }
- return null;
- }
-
- private void cleanupAudioManager() {
- try {
- mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- mVolumeLineItems.clear();
- mCarAudioManager = null;
- }
-
- private final class H extends Handler {
- private static final int SHOW = 1;
- private static final int DISMISS = 2;
-
- public H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case SHOW:
- showH(msg.arg1);
- break;
- case DISMISS:
- dismissH(msg.arg1);
- break;
- default:
- }
- }
- }
-
- private final class CustomDialog extends Dialog implements DialogInterface {
- public CustomDialog(Context context) {
- super(context, com.android.systemui.R.style.qs_theme);
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- rescheduleTimeoutH();
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- protected void onStart() {
- super.setCanceledOnTouchOutside(true);
- super.onStart();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (isShowing()) {
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- mHandler.obtainMessage(
- H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
- return true;
- }
- }
- return false;
- }
- }
-
- private final class ExpandIconListener implements View.OnClickListener {
- @Override
- public void onClick(final View v) {
- mExpanded = !mExpanded;
- Animator inAnimator;
- if (mExpanded) {
- for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
- // Adding the items which are not coming from the default item.
- VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- if (volumeItem.defaultItem) {
- // Set progress here due to the progress of seekbar may not be updated.
- volumeItem.listItem.setProgress(volumeItem.progress);
- } else {
- addSeekbarListItem(volumeItem, groupId, 0, null);
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_up);
- } else {
- // Only keeping the default stream if it is not expended.
- Iterator itr = mVolumeLineItems.iterator();
- while (itr.hasNext()) {
- SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
- VolumeItem volumeItem = findVolumeItem(seekbarListItem);
- if (!volumeItem.defaultItem) {
- itr.remove();
- } else {
- // Set progress here due to the progress of seekbar may not be updated.
- seekbarListItem.setProgress(volumeItem.progress);
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_down);
- }
-
- Animator outAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_out);
- inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
- AnimatorSet animators = new AnimatorSet();
- animators.playTogether(outAnimator, inAnimator);
- animators.setTarget(v);
- animators.start();
- mPagedListAdapter.notifyDataSetChanged();
- }
- }
-
- private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
- private final int mVolumeGroupId;
- private final CarAudioManager mCarAudioManager;
-
- private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
- mVolumeGroupId = volumeGroupId;
- mCarAudioManager = carAudioManager;
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (!fromUser) {
- // For instance, if this event is originated from AudioService,
- // we can ignore it as it has already been handled and doesn't need to be
- // sent back down again.
- return;
- }
- try {
- if (mCarAudioManager == null) {
- Log.w(TAG, "Ignoring volume change event because the car isn't connected");
- return;
- }
- mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
- mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {}
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {}
- }
-
- private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
- @Override
- public void onGroupVolumeChanged(int groupId, int flags) {
- VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- int value = getSeekbarValue(mCarAudioManager, groupId);
- // Do not update the progress if it is the same as before. When car audio manager sets its
- // group volume caused by the seekbar progress changed, it also triggers this callback.
- // Updating the seekbar at the same time could block the continuous seeking.
- if (value != volumeItem.progress) {
- volumeItem.listItem.setProgress(value);
- volumeItem.progress = value;
- }
- if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
- show(Events.SHOW_REASON_VOLUME_CHANGED);
- }
- }
-
- @Override
- public void onMasterMuteChanged(int flags) {
- // ignored
- }
- };
-
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mExpanded = false;
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
- // Populates volume slider items from volume groups to UI.
- for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
- VolumeItem volumeItem = getVolumeItemForUsages(
- mCarAudioManager.getUsagesForVolumeGroupId(groupId));
- mAvailableVolumeItems.add(volumeItem);
- // The first one is the default item.
- if (groupId == 0) {
- volumeItem.defaultItem = true;
- addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down,
- new ExpandIconListener());
- }
- }
-
- // If list is already initiated, update its content.
- if (mPagedListAdapter != null) {
- mPagedListAdapter.notifyDataSetChanged();
- }
- mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- }
-
- /**
- * This does not get called when service is properly disconnected.
- * So we need to also handle cleanups in destroy().
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- cleanupAudioManager();
- }
- };
-
- /**
- * Wrapper class which contains information of each volume group.
- */
- private static class VolumeItem {
- private @AudioAttributes.AttributeUsage int usage;
- private int rank;
- private boolean defaultItem = false;
- private @DrawableRes int icon;
- private SeekbarListItem listItem;
- private int progress;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 2861dff..0805677 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -19,12 +19,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.media.VolumePolicy;
import android.os.Bundle;
-import android.os.Handler;
import android.view.WindowManager.LayoutParams;
import com.android.settingslib.applications.InterestingConfigChanges;
@@ -57,7 +55,7 @@
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
private final SystemUI mSysui;
- private final Context mContext;
+ protected final Context mContext;
private final VolumeDialogControllerImpl mController;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
@@ -70,7 +68,7 @@
400 // vibrateToSilentDebounce
);
- public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) {
+ public VolumeDialogComponent(SystemUI sysui, Context context) {
mSysui = sysui;
mContext = context;
mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
@@ -81,7 +79,6 @@
Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
.withPlugin(VolumeDialog.class)
.withDefault(this::createDefault)
- .withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault)
.withCallback(dialog -> {
if (mDialog != null) {
mDialog.destroy();
@@ -94,7 +91,7 @@
VOLUME_SILENT_DO_NOT_DISTURB);
}
- private VolumeDialog createDefault() {
+ protected VolumeDialog createDefault() {
VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
@@ -102,10 +99,6 @@
return impl;
}
- private VolumeDialog createCarDefault() {
- return new CarVolumeDialogImpl(mContext);
- }
-
@Override
public void onTuningChanged(String key, String newValue) {
if (VOLUME_DOWN_SILENT.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e4f37de..f8cf793 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -22,6 +22,7 @@
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.qs.tiles.DndTile;
import java.io.FileDescriptor;
@@ -43,7 +44,9 @@
mContext.getResources().getBoolean(R.bool.enable_safety_warning);
mEnabled = enableVolumeUi || enableSafetyWarning;
if (!mEnabled) return;
- mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
+
+ mVolumeComponent = SystemUIFactory.getInstance()
+ .createVolumeDialogComponent(this, mContext);
mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
putComponent(VolumeComponent.class, getVolumeComponent());
setDefaultVolumeController();
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index b32bf99..83ec33c 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -38,8 +38,6 @@
android.test.runner \
telephony-common \
android.test.base \
- android.car \
- android.car.userlib
LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 9cbe415..7ca5423 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -51,7 +51,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class KeyguardClockSwitchTest extends SysuiTestCase {
private PluginManager mPluginManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
index 359832f..58870e4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
@@ -38,7 +38,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class KeyguardPinBasedInputViewTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index b98ce39..77895c9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -19,9 +19,14 @@
import android.net.Uri;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -34,12 +39,8 @@
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;
-import androidx.slice.SliceProvider;
-import androidx.slice.SliceSpecs;
-import androidx.slice.builders.ListBuilder;
-
@SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class KeyguardSliceViewTest extends SysuiTestCase {
private KeyguardSliceView mKeyguardSliceView;
@@ -47,6 +48,7 @@
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
mKeyguardSliceView = (KeyguardSliceView) LayoutInflater.from(getContext())
.inflate(R.layout.keyguard_status_area, null);
mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index 9e96df2..3582ab0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -20,10 +20,12 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -32,7 +34,7 @@
import org.mockito.Mock;
@SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class KeyguardStatusViewTest extends SysuiTestCase {
@@ -45,6 +47,7 @@
@Before
public void setUp() {
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
mKeyguardStatusView =
(KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index c180ff8..948e001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -16,29 +16,29 @@
package com.android.systemui;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.animation.ObjectAnimator;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class ExpandHelperTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
@@ -47,6 +47,7 @@
@Before
public void setUp() throws Exception {
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
Context context = getContext();
mRow = new NotificationTestHelper(context).createRow();
mCallback = mock(ExpandHelper.Callback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index a58bc85..9c5a592 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -29,6 +29,7 @@
import android.util.Log;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.util.Assert;
import org.junit.After;
import org.junit.Before;
@@ -78,6 +79,8 @@
public void SysuiTeardown() {
InstrumentationRegistry.registerInstance(mRealInstrumentation,
InstrumentationRegistry.getArguments());
+ // Reset the assert's main looper.
+ Assert.sMainLooper = Looper.getMainLooper();
}
protected LeakCheck getLeakCheck() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index e811270..f792d7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -18,6 +18,7 @@
import android.testing.LeakCheck;
import android.testing.TestableContext;
import android.util.ArrayMap;
+import android.view.Display;
public class SysuiTestableContext extends TestableContext implements SysUiServiceProvider {
@@ -47,4 +48,15 @@
if (mComponents == null) mComponents = new ArrayMap<>();
mComponents.put(interfaceType, component);
}
+
+ @Override
+ public Context createDisplayContext(Display display) {
+ if (display == null) {
+ throw new IllegalArgumentException("display must not be null");
+ }
+
+ SysuiTestableContext context =
+ new SysuiTestableContext(getBaseContext().createDisplayContext(display));
+ return context;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
new file mode 100644
index 0000000..16ef63f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.IActivityManager;
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleControllerTest extends SysuiTestCase {
+
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private IActivityManager mActivityManager;
+ @Mock
+ private DozeParameters mDozeParameters;
+ @Mock
+ private FrameLayout mStatusBarView;
+
+ private TestableBubbleController mBubbleController;
+ private StatusBarWindowController mStatusBarWindowController;
+
+ private NotificationTestHelper mNotificationTestHelper;
+ private ExpandableNotificationRow mRow;
+ private ExpandableNotificationRow mRow2;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ // Bubbles get added to status bar window view
+ mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
+ mActivityManager, mDozeParameters);
+ mStatusBarWindowController.add(mStatusBarView, 120 /* height */);
+
+ // Need notifications for bubbles
+ mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mRow = mNotificationTestHelper.createBubble();
+ mRow2 = mNotificationTestHelper.createBubble();
+
+ mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+ }
+
+ @Test
+ public void testIsBubble() {
+ assertTrue(mRow.getEntry().isBubble());
+ }
+
+ @Test
+ public void testAddBubble() {
+ mBubbleController.addBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void testHasBubbles() {
+ assertFalse(mBubbleController.hasBubbles());
+ mBubbleController.addBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void testRemoveBubble() {
+ mBubbleController.addBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+
+ mBubbleController.removeBubble(mRow.getEntry().key);
+ assertFalse(mStatusBarWindowController.getBubblesShowing());
+ }
+
+ @Test
+ public void testDismissStack() {
+ mBubbleController.addBubble(mRow.getEntry());
+ mBubbleController.addBubble(mRow2.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+
+ mBubbleController.dismissStack();
+ assertFalse(mStatusBarWindowController.getBubblesShowing());
+ }
+
+ @Test
+ public void testIsStackExpanded() {
+ assertFalse(mBubbleController.isStackExpanded());
+ mBubbleController.addBubble(mRow.getEntry());
+
+ BubbleStackView stackView = mBubbleController.getStackView();
+ stackView.animateExpansion(true /* expanded */);
+ assertTrue(mBubbleController.isStackExpanded());
+
+ stackView.animateExpansion(false /* expanded */);
+ assertFalse(mBubbleController.isStackExpanded());
+ }
+
+ @Test
+ public void testCollapseStack() {
+ mBubbleController.addBubble(mRow.getEntry());
+ mBubbleController.addBubble(mRow2.getEntry());
+
+ BubbleStackView stackView = mBubbleController.getStackView();
+ stackView.animateExpansion(true /* expanded */);
+ assertTrue(mBubbleController.isStackExpanded());
+
+ mBubbleController.collapseStack();
+ assertFalse(mBubbleController.isStackExpanded());
+ }
+
+ static class TestableBubbleController extends BubbleController {
+
+ TestableBubbleController(Context context,
+ StatusBarWindowController statusBarWindowController) {
+ super(context);
+ mStatusBarWindowController = statusBarWindowController;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 368c814f..e1c481e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -28,8 +28,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -40,14 +40,12 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.view.Display;
+import android.testing.UiThreadTest;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.wakelock.WakeLockFake;
-import android.testing.UiThreadTest;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 5c8336c..31fc625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -48,7 +48,7 @@
@SmallTest
@Ignore("failing")
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class DozeTriggersTest extends SysuiTestCase {
private DozeTriggers mTriggers;
private DozeMachine mMachine;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 0953951..c1c80ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -55,7 +55,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class KeyguardSliceProviderTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 4e24354..bc7d983 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -18,39 +18,32 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
-import android.app.FragmentController;
-import android.app.FragmentManagerNonConfig;
+import android.content.Context;
import android.os.Looper;
-import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-
-import android.os.Parcelable;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import android.testing.LayoutInflaterBuilder;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import android.content.Context;
-import android.view.View;
-import android.widget.FrameLayout;
-
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
@SmallTest
@Ignore
public class QSFragmentTest extends SysuiBaseFragmentTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 33b347a..fd31013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.pm.UserInfo;
-import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
@@ -58,7 +57,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class QSSecurityFooterTest extends SysuiTestCase {
private final String MANAGING_ORGANIZATION = "organization";
@@ -76,7 +75,8 @@
@Before
public void setUp() {
mDependency.injectTestDependency(SecurityController.class, mSecurityController);
- mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+ mDependency.injectTestDependency(Dependency.BG_LOOPER,
+ TestableLooper.get(this).getLooper());
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
new LayoutInflaterBuilder(mContext)
.replace("ImageView", TestableImageView.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
new file mode 100644
index 0000000..78700b8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+
+import static junit.framework.TestCase.assertFalse;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class QSTileHostTest extends SysuiTestCase {
+
+ @Test
+ public void testLoadTileSpecs_emptySetting() {
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
+ assertFalse(tiles.isEmpty());
+ }
+
+ @Test
+ public void testLoadTileSpecs_nullSetting() {
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
+ assertFalse(tiles.isEmpty());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
deleted file mode 100644
index f89a932..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.android.systemui.qs.car;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.LayoutInflaterBuilder;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.keyguard.CarrierText;
-import com.android.systemui.Dependency;
-import com.android.systemui.SysuiBaseFragmentTest;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.Clock;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link CarQSFragment}.
- */
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-@SmallTest
-@Ignore
-public class CarQsFragmentTest extends SysuiBaseFragmentTest {
- public CarQsFragmentTest() {
- super(CarQSFragment.class);
- }
-
- @Before
- public void initDependencies() {
- mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
- new LayoutInflaterBuilder(mContext)
- .replace("com.android.systemui.statusbar.policy.SplitClockView",
- FrameLayout.class)
- .replace("TextClock", View.class)
- .replace(CarrierText.class, View.class)
- .replace(Clock.class, View.class)
- .build());
- mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mDependency.injectTestDependency(Dependency.BG_LOOPER,
- TestableLooper.get(this).getLooper());
- }
-
- @Test
- @Ignore("Flaky")
- public void testLayoutInflation() {
- CarQSFragment fragment = (CarQSFragment) mFragment;
- mFragments.dispatchResume();
-
- assertNotNull(fragment.getHeader());
- assertNotNull(fragment.getFooter());
- }
-
- @Test
- @Ignore("Flaky")
- public void testListening() {
- CarQSFragment qs = (CarQSFragment) mFragment;
- mFragments.dispatchResume();
- processAllMessages();
-
- qs.setListening(true);
- processAllMessages();
-
- qs.setListening(false);
- processAllMessages();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index c016a85..c6597b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -24,16 +24,15 @@
import android.os.Looper;
import android.service.quicksettings.Tile;
import android.test.suitebuilder.annotation.SmallTest;
-
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -45,7 +44,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class TileServicesTest extends SysuiTestCase {
private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 6764634..e5464e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -19,8 +19,10 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -37,7 +39,6 @@
import android.content.Intent;
import android.metrics.LogMaker;
import android.support.test.filters.SmallTest;
-import android.support.test.InstrumentationRegistry;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -48,12 +49,17 @@
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
+import org.mockito.Captor;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -65,13 +71,20 @@
private TileImpl mTile;
private QSTileHost mHost;
private MetricsLogger mMetricsLogger;
+ private StatusBarStateController mStatusBarStateController;
+
+ @Captor
+ private ArgumentCaptor<LogMaker> mLogCaptor;
@Before
public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
String spec = "spec";
mTestableLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
+ mStatusBarStateController =
+ mDependency.injectMockDependency(StatusBarStateController.class);
mHost = mock(QSTileHost.class);
when(mHost.indexOf(spec)).thenReturn(POSITION);
when(mHost.getContext()).thenReturn(mContext.getBaseContext());
@@ -88,19 +101,57 @@
}
@Test
+ public void testClick_Metrics_Status_Bar_Status() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+ mTile.click();
+ verify(mMetricsLogger).write(mLogCaptor.capture());
+ assertEquals(StatusBarState.SHADE, mLogCaptor.getValue()
+ .getTaggedData(FIELD_STATUS_BAR_STATE));
+ }
+
+ @Test
public void testSecondaryClick_Metrics() {
mTile.secondaryClick();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
}
@Test
+ public void testSecondaryClick_Metrics_Status_Bar_Status() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ mTile.secondaryClick();
+ verify(mMetricsLogger).write(mLogCaptor.capture());
+ assertEquals(StatusBarState.KEYGUARD, mLogCaptor.getValue()
+ .getTaggedData(FIELD_STATUS_BAR_STATE));
+ }
+
+ @Test
public void testLongClick_Metrics() {
mTile.longClick();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
}
@Test
- public void testPopulate() {
+ public void testLongClick_Metrics_Status_Bar_Status() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+ mTile.click();
+ verify(mMetricsLogger).write(mLogCaptor.capture());
+ assertEquals(StatusBarState.SHADE_LOCKED, mLogCaptor.getValue()
+ .getTaggedData(FIELD_STATUS_BAR_STATE));
+ }
+
+ @Test
+ public void testPopulateWithLockedScreen() {
+ LogMaker maker = mock(LogMaker.class);
+ when(maker.setSubtype(anyInt())).thenReturn(maker);
+ when(maker.addTaggedData(anyInt(), any())).thenReturn(maker);
+ mTile.getState().value = true;
+ mTile.populate(maker);
+ verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1));
+ verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
+ }
+
+ @Test
+ public void testPopulateWithUnlockedScreen() {
LogMaker maker = mock(LogMaker.class);
when(maker.setSubtype(anyInt())).thenReturn(maker);
when(maker.addTaggedData(anyInt(), any())).thenReturn(maker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 8ae3cd8..a04e57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -42,7 +44,7 @@
public void setup() {
mCommandQueue = new CommandQueue();
mCallbacks = mock(Callbacks.class);
- mCommandQueue.addCallbacks(mCallbacks);
+ mCommandQueue.addCallback(mCallbacks);
verify(mCallbacks).disable(eq(0), eq(0), eq(false));
}
@@ -64,11 +66,12 @@
verify(mCallbacks).removeIcon(eq(slot));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testDisable() {
int state1 = 14;
int state2 = 42;
- mCommandQueue.disable(state1, state2);
+ mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2);
waitForIdleSync();
verify(mCallbacks).disable(eq(state1), eq(state2), eq(true));
}
@@ -95,24 +98,27 @@
verify(mCallbacks).animateExpandSettingsPanel(eq(panel));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testSetSystemUiVisibility() {
Rect r = new Rect();
- mCommandQueue.setSystemUiVisibility(1, 2, 3, 4, null, r);
+ mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
waitForIdleSync();
verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testTopAppWindowChanged() {
- mCommandQueue.topAppWindowChanged(true);
+ mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
waitForIdleSync();
verify(mCallbacks).topAppWindowChanged(eq(true));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testShowImeButton() {
- mCommandQueue.setImeWindowStatus(null, 1, 2, true);
+ mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
waitForIdleSync();
verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true));
}
@@ -166,9 +172,10 @@
verify(mCallbacks).toggleKeyboardShortcutsMenu(eq(1));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testSetWindowState() {
- mCommandQueue.setWindowState(1, 2);
+ mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2);
waitForIdleSync();
verify(mCallbacks).setWindowState(eq(1), eq(2));
}
@@ -180,30 +187,34 @@
verify(mCallbacks).showScreenPinningRequest(eq(1));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionPending() {
- mCommandQueue.appTransitionPending();
+ mCommandQueue.appTransitionPending(DEFAULT_DISPLAY);
waitForIdleSync();
verify(mCallbacks).appTransitionPending(eq(false));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionCancelled() {
- mCommandQueue.appTransitionCancelled();
+ mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY);
waitForIdleSync();
verify(mCallbacks).appTransitionCancelled();
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionStarting() {
- mCommandQueue.appTransitionStarting(1, 2);
+ mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2);
waitForIdleSync();
verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionFinished() {
- mCommandQueue.appTransitionFinished();
+ mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
waitForIdleSync();
verify(mCallbacks).appTransitionFinished();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 2e280d3..f8ff583 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -50,7 +50,7 @@
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
@Ignore("b/118400112")
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@@ -91,7 +91,7 @@
mCheckSaveListener, mOnSettingsClickListener);
notificationLogger.setUpWithContainer(mListContainer);
mediaManager.setUpWithPresenter(mPresenter);
- remoteInputManager.setUpWithPresenter(mPresenter, mRemoteInputManagerCallback,
+ remoteInputManager.setUpWithCallback(mRemoteInputManagerCallback,
mDelegate);
lockscreenUserManager.setUpWithPresenter(mPresenter);
viewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 63ececb..65c04fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -16,15 +16,12 @@
package com.android.systemui.statusbar;
-import static junit.framework.Assert.assertTrue;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -45,7 +42,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class NotificationListenerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index b5d305d..bee931f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -22,13 +22,12 @@
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender;
import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender;
import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.google.android.collect.Sets;
@@ -80,7 +79,7 @@
mEntry = new NotificationData.Entry(mSbn);
mEntry.setRow(mRow);
- mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
+ mRemoteInputManager.setUpWithPresenterForTest(mCallback,
mDelegate, mController);
for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) {
extender.setCallback(
@@ -201,11 +200,10 @@
super(context);
}
- public void setUpWithPresenterForTest(NotificationPresenter presenter,
- Callback callback,
+ public void setUpWithPresenterForTest(Callback callback,
RemoteInputController.Delegate delegate,
RemoteInputController controller) {
- super.setUpWithPresenter(presenter, callback, delegate);
+ super.setUpWithCallback(callback, delegate);
mRemoteInputController = controller;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 9bed59b..fb5e875 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -33,9 +33,9 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -86,7 +86,8 @@
* @throws Exception
*/
public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
- return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
+ return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */,
+ false /* isBubble */);
}
/**
@@ -97,7 +98,8 @@
* @throws Exception
*/
public ExpandableNotificationRow createRow(Notification notification) throws Exception {
- return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */);
+ return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */,
+ false /* isBubble */);
}
/**
@@ -110,7 +112,8 @@
*/
public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags)
throws Exception {
- return generateRow(createNotification(), PKG, UID, extraInflationFlags);
+ return generateRow(createNotification(), PKG, UID, extraInflationFlags,
+ false /* isBubble */);
}
/**
@@ -131,12 +134,20 @@
return createGroup(2);
}
+ /**
+ * Retursn an {@link ExpandableNotificationRow} that should be a bubble.
+ */
+ public ExpandableNotificationRow createBubble() throws Exception {
+ return createRow(PKG, UID, false /* isGroupSummary */, null /* groupKey */,
+ true /* isBubble */);
+ }
+
private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception {
- return createRow(PKG, UID, true /* isGroupSummary */, groupkey);
+ return createRow(PKG, UID, true /* isGroupSummary */, groupkey, false);
}
private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception {
- return createRow(PKG, UID, false /* isGroupSummary */, groupkey);
+ return createRow(PKG, UID, false /* isGroupSummary */, groupkey, false);
}
/**
@@ -146,6 +157,7 @@
* @param uid uid used for creating a {@link StatusBarNotification}
* @param isGroupSummary whether the notification row is a group summary
* @param groupKey the group key for the notification group used across notifications
+ * @param isBubble
* @return a row with that's either a standalone notification or a group notification if the
* groupKey is non-null
* @throws Exception
@@ -154,10 +166,10 @@
String pkg,
int uid,
boolean isGroupSummary,
- @Nullable String groupKey)
+ @Nullable String groupKey, boolean isBubble)
throws Exception {
Notification notif = createNotification(isGroupSummary, groupKey);
- return generateRow(notif, pkg, uid, 0 /* inflationFlags */);
+ return generateRow(notif, pkg, uid, 0 /* inflationFlags */, isBubble);
}
/**
@@ -202,7 +214,7 @@
Notification notification,
String pkg,
int uid,
- @InflationFlag int extraInflationFlags)
+ @InflationFlag int extraInflationFlags, boolean isBubble)
throws Exception {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
@@ -232,6 +244,7 @@
entry.channel = new NotificationChannel(
notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
entry.channel.setBlockableSystem(true);
+ entry.setIsBubble(isBubble);
row.setEntry(entry);
row.getNotificationInflater().addInflationFlags(extraInflationFlags);
NotificationInflaterTest.runThenWaitForInflation(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 72d6cd8..520a927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -46,6 +46,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.util.Assert;
import com.google.android.collect.Lists;
@@ -60,7 +61,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationData mNotificationData;
@@ -79,6 +80,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
mLockscreenUserManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 76b992f..8d52ccd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -76,7 +76,7 @@
mSmartReplyController);
mRemoteInputManager = new NotificationRemoteInputManager(mContext);
- mRemoteInputManager.setUpWithPresenter(mPresenter, mCallback, mDelegate);
+ mRemoteInputManager.setUpWithCallback(mCallback, mDelegate);
mNotification = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index b7aa21b..db2c878 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -19,15 +19,15 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.widget.FrameLayout;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Assert;
import org.junit.Before;
@@ -36,7 +36,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class AboveShelfObserverTest extends SysuiTestCase {
private AboveShelfObserver mObserver;
@@ -46,6 +46,7 @@
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
mNotificationTestHelper = new NotificationTestHelper(getContext());
mHostLayout = new FrameLayout(getContext());
mObserver = new AboveShelfObserver(mHostLayout);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index f94ba95..8e88ed0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -53,6 +53,7 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArraySet;
@@ -78,7 +79,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class NotificationDataTest extends SysuiTestCase {
private static final int UID_NORMAL = 123;
@@ -101,6 +102,7 @@
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
index 63d1e8d..24aa772 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -17,28 +17,29 @@
package com.android.systemui.statusbar.notification;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.util.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
@SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class NotificationViewWrapperTest extends SysuiTestCase {
@Test
public void constructor_doesntUseViewContext() throws Exception {
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
new TestableNotificationViewWrapper(mContext,
new View(mContext),
new NotificationTestHelper(getContext()).createRow());
@@ -50,4 +51,4 @@
super(ctx, view, row);
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 3710fa8..512acd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -37,13 +37,13 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
-
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+
import com.google.android.collect.Lists;
import org.junit.Before;
@@ -58,7 +58,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class NotificationLoggerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 2da72e7..6d35539 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -40,6 +40,7 @@
import android.app.NotificationChannel;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
@@ -64,7 +65,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class ExpandableNotificationRowTest extends SysuiTestCase {
private ExpandableNotificationRow mGroupRow;
@@ -77,6 +78,7 @@
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
mNotificationTestHelper = new NotificationTestHelper(mContext);
mGroupRow = mNotificationTestHelper.createGroup();
mGroupRow.setHeadsUpAnimatingAwayListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 4efab53..669b98e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -16,26 +16,10 @@
package com.android.systemui.statusbar.notification.row;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
-
-import android.content.Context;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -45,13 +29,31 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.util.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
/**
* Tests for {@link NotificationBlockingHelperManager}.
*/
@SmallTest
@FlakyTest
@org.junit.runner.RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
private NotificationBlockingHelperManager mBlockingHelperManager;
@@ -65,6 +67,7 @@
@Before
public void setUp() {
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
when(mGutsManager.openGuts(
any(View.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 766c5d2..ad43bea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -59,11 +59,12 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -79,7 +80,7 @@
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class NotificationGutsManagerTest extends SysuiTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
@@ -92,7 +93,7 @@
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private NotificationPresenter mPresenter;
- @Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotificationActivityStarter mNotificationActivityStarter;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
@Mock private OnSettingsClickListener mOnSettingsClickListener;
@@ -101,6 +102,7 @@
@Before
public void setUp() {
mTestableLooper = TestableLooper.get(this);
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
@@ -110,6 +112,7 @@
mGutsManager = new NotificationGutsManager(mContext);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
+ mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
}
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -217,7 +220,8 @@
ops.add(OP_CAMERA);
mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+ verify(mNotificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), any());
assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
}
@@ -227,7 +231,8 @@
ops.add(OP_RECORD_AUDIO);
mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+ verify(mNotificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), any());
assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
}
@@ -238,7 +243,8 @@
ops.add(OP_RECORD_AUDIO);
mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+ verify(mNotificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), any());
assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
}
@@ -248,7 +254,8 @@
ops.add(OP_SYSTEM_ALERT_WINDOW);
mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+ verify(mNotificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), any());
assertEquals(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, captor.getValue().getAction());
}
@@ -260,7 +267,8 @@
ops.add(OP_SYSTEM_ALERT_WINDOW);
mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+ verify(mNotificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), any());
assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
}
@@ -271,7 +279,8 @@
ops.add(OP_SYSTEM_ALERT_WINDOW);
mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+ verify(mNotificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), any());
assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
}
@@ -282,7 +291,8 @@
ops.add(OP_SYSTEM_ALERT_WINDOW);
mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+ verify(mNotificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), any());
assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 906e718..e4d0196 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -25,7 +25,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Notification;
+import android.app.NotificationChannel;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -35,6 +35,7 @@
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
@@ -43,19 +44,26 @@
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper()
+@RunWithLooper
@SmallTest
public class NotificationMenuRowTest extends LeakCheckedTest {
+ private ExpandableNotificationRow mRow;
+
@Before
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+ mRow = mock(ExpandableNotificationRow.class);
+ NotificationData.Entry entry = new NotificationData.Entry(
+ mock(StatusBarNotification.class));
+ entry.channel = mock(NotificationChannel.class);
+ when(mRow.getEntry()).thenReturn(entry);
}
@Test
public void testAttachDetach() {
NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
- row.createMenu(null, null);
+ row.createMenu(mRow, null);
ViewUtils.attachView(row.getMenuView());
TestableLooper.get(this).processAllMessages();
ViewUtils.detachView(row.getMenuView());
@@ -65,9 +73,9 @@
@Test
public void testRecreateMenu() {
NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
- row.createMenu(null, null);
+ row.createMenu(mRow, null);
assertTrue(row.getMenuView() != null);
- row.createMenu(null, null);
+ row.createMenu(mRow, null);
assertTrue(row.getMenuView() != null);
}
@@ -81,12 +89,7 @@
@Test
public void testNoAppOpsInSlowSwipe() {
NotificationMenuRow row = new NotificationMenuRow(mContext);
- Notification n = mock(Notification.class);
- StatusBarNotification sbn = mock(StatusBarNotification.class);
- when(sbn.getNotification()).thenReturn(n);
- ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
- when(parent.getStatusBarNotification()).thenReturn(sbn);
- row.createMenu(parent, null);
+ row.createMenu(mRow, null);
ViewGroup container = (ViewGroup) row.getMenuView();
// one for snooze and one for noti blocking
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 4b94a25..fed66af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -18,6 +18,7 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.RemoteViews;
@@ -34,13 +35,14 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class NotificationCustomViewWrapperTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
mRow = new NotificationTestHelper(mContext).createRow();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 2728453..bbafb4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -18,13 +18,14 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
import android.view.View;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Assert;
import org.junit.Before;
@@ -33,16 +34,16 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class NotificationChildrenContainerTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
- private int mId;
private NotificationTestHelper mNotificationTestHelper;
private NotificationChildrenContainer mChildrenContainer;
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
mNotificationTestHelper = new NotificationTestHelper(mContext);
mGroup = mNotificationTestHelper.createGroup();
mChildrenContainer = mGroup.getChildrenContainer();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 662e016..8ae7d52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -25,6 +25,7 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.systemui.SysuiTestCase;
@@ -41,7 +42,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class NotificationRoundnessManagerTest extends SysuiTestCase {
private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
@@ -52,6 +53,7 @@
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
mFirst.setHeadsUpAnimatingAwayListener(animatingAway
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index c99e766..4f6329c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -23,18 +23,18 @@
import static org.mockito.Mockito.when;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.TextView;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import org.junit.Assert;
import org.junit.Before;
@@ -43,7 +43,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
private final NotificationStackScrollLayout mStackScroller =
@@ -58,6 +58,7 @@
@Before
public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 7f8668f..f7a95c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.content.res.ColorStateList;
import android.graphics.Color;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -35,7 +36,6 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
-import android.content.res.ColorStateList;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardHostView;
@@ -56,7 +56,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class KeyguardBouncerTest extends SysuiTestCase {
@Mock
@@ -78,6 +78,7 @@
@Before
public void setup() {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
DejankUtils.setImmediate(true);
final ViewGroup container = new FrameLayout(getContext());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 31014b8..27ed9c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -30,7 +30,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private static final int SCREEN_HEIGHT = 2000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java
index 5429153..c587555 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java
@@ -20,21 +20,20 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
-import android.view.View;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class KeyguardPresentationTest extends SysuiTestCase {
@Test
public void testInflation_doesntCrash() {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.keyguard_presentation, null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
new file mode 100644
index 0000000..73f3b43
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.statusbar.CommandQueue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** atest NavigationBarButtonTest */
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class NavigationBarButtonTest extends SysuiTestCase {
+
+ private ImageReader mReader;
+ private NavigationBarView mNavBar;
+ private VirtualDisplay mVirtualDisplay;
+
+ @Before
+ public void setup() {
+ final Display display = createVirtualDisplay();
+ final SysuiTestableContext context =
+ (SysuiTestableContext) mContext.createDisplayContext(display);
+ context.putComponent(CommandQueue.class, mock(CommandQueue.class));
+
+ mNavBar = new NavigationBarView(context, null);
+ }
+
+ private Display createVirtualDisplay() {
+ final String displayName = "NavVirtualDisplay";
+ final DisplayInfo displayInfo = new DisplayInfo();
+ mContext.getDisplay().getDisplayInfo(displayInfo);
+
+ final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ mReader = ImageReader.newInstance(displayInfo.logicalWidth,
+ displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+ assertNotNull("ImageReader must not be null", mReader);
+
+ mVirtualDisplay = displayManager.createVirtualDisplay(displayName, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, displayInfo.logicalDensityDpi, mReader.getSurface(),
+ 0 /*flags*/);
+
+ assertNotNull("virtual display must not be null", mVirtualDisplay);
+
+ return mVirtualDisplay.getDisplay();
+ }
+
+ @After
+ public void tearDown() {
+ releaseDisplay();
+ }
+
+ private void releaseDisplay() {
+ mVirtualDisplay.release();
+ mReader.close();
+ }
+
+ @Test
+ public void testRecentsButtonDisabledOnSecondaryDisplay() {
+ assertTrue("The recents button must be disabled",
+ mNavBar.isRecentsButtonDisabled());
+ }
+}
+
+
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index 8fc15b2..2805908 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -29,7 +29,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -38,11 +37,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import com.android.systemui.R;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.SysuiTestCase;
-
import android.content.Context;
import android.content.res.Resources;
import android.support.test.filters.SmallTest;
@@ -51,6 +45,11 @@
import android.view.MotionEvent;
import android.view.View;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.IOverviewProxy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,9 +58,13 @@
/** atest QuickStepControllerTest */
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
@SmallTest
public class QuickStepControllerTest extends SysuiTestCase {
+ private static final int NAVBAR_WIDTH = 1000;
+ private static final int NAVBAR_HEIGHT = 300;
+ private static final int EDGE_THRESHOLD = 100;
+
private QuickStepController mController;
private NavigationBarView mNavigationBarView;
private StatusBar mStatusBar;
@@ -74,10 +77,13 @@
MockitoAnnotations.initMocks(this);
final ButtonDispatcher backButton = mock(ButtonDispatcher.class);
mResources = mock(Resources.class);
+ doReturn(EDGE_THRESHOLD).when(mResources)
+ .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold);
mProxyService = mock(OverviewProxyService.class);
mProxy = mock(IOverviewProxy.Stub.class);
doReturn(mProxy).when(mProxyService).getProxy();
+ doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
mStatusBar = mock(StatusBar.class);
@@ -106,13 +112,27 @@
}
@Test
+ public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
+ doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
+ mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
+
+ MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
+ assertFalse(mController.onInterceptTouchEvent(ev));
+ verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
+ assertNull(mController.getCurrentAction());
+ }
+
+ @Test
public void testHasActionDetectGesturesTouchdown() throws Exception {
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
// Add enabled gesture action
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
assertFalse(mController.onInterceptTouchEvent(ev));
verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
@@ -128,7 +148,8 @@
// Add enabled gesture action
mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
// Set the gesture on deadzone
doReturn(null).when(mProxyService).getProxy();
@@ -153,7 +174,8 @@
@Test
public void testOnTouchIgnoredDownEventAfterOnIntercept() {
mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
assertFalse(touch(ev));
@@ -166,29 +188,45 @@
@Test
public void testGesturesCallCorrectAction() throws Exception {
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
+
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+ NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+ NavigationGestureAction swipeRightFromEdge = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+ swipeRightFromEdge);
// Swipe Up
assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
// Swipe Down
assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
// Swipe Left
- assertGestureTriggersAction(swipeLeft, 100, 1, 5, 1);
+ assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1);
// Swipe Right
- assertGestureTriggersAction(swipeRight, 1, 1, 100, 5);
+ assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5);
+ // Swipe Left from Edge
+ assertGestureTriggersAction(swipeLeftFromEdge, NAVBAR_WIDTH, 1, 5, 1);
+ // Swipe Right from Edge
+ assertGestureTriggersAction(swipeRightFromEdge, 0, 1, NAVBAR_WIDTH, 5);
}
@Test
public void testGesturesCallCorrectActionLandscape() throws Exception {
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
+
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+ NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+ NavigationGestureAction swipeRightFromEdge = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+ swipeRightFromEdge);
// In landscape
mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT);
@@ -196,34 +234,50 @@
// Swipe Up
assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
// Swipe Down
- assertGestureTriggersAction(swipeLeft, 1, 1, 5, 100);
+ assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
+ // Swipe Up from Edge
+ assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+ // Swipe Down from Edge
+ assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
public void testGesturesCallCorrectActionSeascape() throws Exception {
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
+
mController.setBarState(false /* isRTL */, NAV_BAR_LEFT);
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+ NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+ NavigationGestureAction swipeRightFromEdge = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+ swipeRightFromEdge);
// Swipe Up
- assertGestureTriggersAction(swipeLeft, 1, 100, 5, 1);
+ assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
// Swipe Down
- assertGestureTriggersAction(swipeRight, 1, 1, 5, 100);
+ assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
+ // Swipe Up from Edge
+ assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+ // Swipe Down from Edge
+ assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
public void testGesturesCallCorrectActionRTL() throws Exception {
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
mController.setBarState(true /* isRTL */, NAV_BAR_BOTTOM);
// The swipe gestures below are for LTR, so RTL in portrait will be swapped
@@ -231,20 +285,29 @@
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+ NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+ NavigationGestureAction swipeRightFromEdge = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+ swipeRightFromEdge);
// Swipe Up in RTL
assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
// Swipe Down in RTL
assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
// Swipe Left in RTL
- assertGestureTriggersAction(swipeRight, 100, 1, 5, 1);
+ assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1);
// Swipe Right in RTL
- assertGestureTriggersAction(swipeLeft, 1, 1, 100, 5);
+ assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0);
+ // Swipe Left from Edge
+ assertGestureTriggersAction(swipeRightFromEdge, NAVBAR_WIDTH, 1, 5, 1);
+ // Swipe Right from Edge
+ assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, NAVBAR_WIDTH, 5);
}
@Test
public void testGesturesCallCorrectActionLandscapeRTL() throws Exception {
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
mController.setBarState(true /* isRTL */, NAV_BAR_RIGHT);
// The swipe gestures below are for LTR, so RTL in landscape will be swapped
@@ -252,20 +315,29 @@
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+ NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+ NavigationGestureAction swipeRightFromEdge = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+ swipeRightFromEdge);
// Swipe Up
- assertGestureTriggersAction(swipeLeft, 1, 100, 5, 1);
+ assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
// Swipe Down
- assertGestureTriggersAction(swipeRight, 1, 1, 5, 100);
+ assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
+ // Swipe Up from Edge
+ assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+ // Swipe Down from Edge
+ assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
public void testGesturesCallCorrectActionSeascapeRTL() throws Exception {
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
mController.setBarState(true /* isRTL */, NAV_BAR_LEFT);
// The swipe gestures below are for LTR, so RTL in seascape will be swapped
@@ -273,16 +345,23 @@
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+ NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+ NavigationGestureAction swipeRightFromEdge = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+ swipeRightFromEdge);
// Swipe Up
- assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
+ assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1);
// Swipe Down
- assertGestureTriggersAction(swipeLeft, 1, 1, 5, 100);
+ assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
+ // Swipe Up from Edge
+ assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+ // Swipe Down from Edge
+ assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
@@ -293,7 +372,8 @@
// Add enabled gesture action
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
// Touch down to begin swipe
MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
@@ -314,7 +394,8 @@
NavigationGestureAction action = mockAction(true);
doReturn(false).when(action).canRunWhenNotificationsShowing();
mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
// Show the notifications
doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
@@ -339,7 +420,8 @@
public void testActionCannotPerform() throws Exception {
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
// Cannot perform action
doReturn(false).when(action).canPerformAction();
@@ -362,13 +444,17 @@
@Test
public void testQuickScrub() throws Exception {
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
QuickScrubAction action = spy(new QuickScrubAction(mNavigationBarView, mProxyService));
mController.setGestureActions(null /* swipeUpAction */, null /* swipeDownAction */,
- null /* swipeLeftAction */, action);
+ null /* swipeLeftAction */, action, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
+ int x = NAVBAR_WIDTH / 2;
int y = 20;
// Set the layout and other padding to make sure the scrub fraction is calculated correctly
- action.onLayout(true, 0, 0, 400, 100);
+ action.onLayout(true, 0, 0, NAVBAR_WIDTH, NAVBAR_HEIGHT);
doReturn(0).when(mNavigationBarView).getPaddingLeft();
doReturn(0).when(mNavigationBarView).getPaddingRight();
doReturn(0).when(mNavigationBarView).getPaddingStart();
@@ -381,14 +467,14 @@
doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
// Touch down
- MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 0, y);
+ MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, x, y);
assertFalse(touch(downEvent));
assertNull(mController.getCurrentAction());
verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
verify(mProxy, times(1)).onMotionEvent(downEvent);
// Move to start trigger action from gesture
- MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, 100, y);
+ MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, x + 100, y);
assertTrue(touch(moveEvent1));
assertEquals(action, mController.getCurrentAction());
verify(action, times(1)).onGestureStart(moveEvent1);
@@ -398,11 +484,13 @@
verify(mProxy, never()).onMotionEvent(moveEvent1);
// Move again for scrub
- MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, 200, y);
+ float fraction = 3f / 4;
+ x = (int) (NAVBAR_WIDTH * fraction);
+ MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, x, y);
assertTrue(touch(moveEvent2));
assertEquals(action, mController.getCurrentAction());
- verify(action, times(1)).onGestureMove(200, y);
- verify(mProxy, times(1)).onQuickScrubProgress(1f / 2);
+ verify(action, times(1)).onGestureMove(x, y);
+ verify(mProxy, times(1)).onQuickScrubProgress(fraction);
verify(mProxy, never()).onMotionEvent(moveEvent2);
// Action up
@@ -418,7 +506,8 @@
public void testQuickStep() throws Exception {
QuickStepAction action = new QuickStepAction(mNavigationBarView, mProxyService);
mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
// Notifications are up, should prevent quickstep
doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
@@ -454,7 +543,8 @@
public void testLongPressPreventDetection() throws Exception {
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */);
+ null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+ null /* rightEdgeSwipe */);
// Start the drag up
assertFalse(touch(MotionEvent.ACTION_DOWN, 100, 1));
@@ -476,23 +566,21 @@
@Test
public void testHitTargetDragged() throws Exception {
- final int navbarWidth = 1000;
- final int navbarHeight = 1000;
ButtonDispatcher button = mock(ButtonDispatcher.class);
- FakeLocationView buttonView = spy(new FakeLocationView(mContext, navbarWidth / 2,
- navbarHeight / 2));
+ FakeLocationView buttonView = spy(new FakeLocationView(mContext, NAVBAR_WIDTH / 2,
+ NAVBAR_HEIGHT / 2));
doReturn(buttonView).when(button).getCurrentView();
NavigationGestureAction action = mockAction(true);
- mController.setGestureActions(action, action, action, action);
+ mController.setGestureActions(action, action, action, action, action, action);
// Setup getting the hit target
doReturn(HIT_TARGET_HOME).when(action).requiresTouchDownHitTarget();
- doReturn(true).when(action).requiresDragWithHitTarget();
+ doReturn(true).when(action).allowHitTargetToMoveOverDrag();
doReturn(HIT_TARGET_HOME).when(mNavigationBarView).getDownHitTarget();
doReturn(button).when(mNavigationBarView).getHomeButton();
- doReturn(navbarWidth).when(mNavigationBarView).getWidth();
- doReturn(navbarHeight).when(mNavigationBarView).getHeight();
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
// Portrait
assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_BOTTOM);
@@ -513,6 +601,25 @@
assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT);
}
+ @Test
+ public void testNoEdgeActionsTriggerNormalActions() {
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
+
+ NavigationGestureAction swipeUp = mockAction(true);
+ NavigationGestureAction swipeDown = mockAction(true);
+ NavigationGestureAction swipeLeft = mockAction(true);
+ NavigationGestureAction swipeRight = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
+ null /* swipeLeftFromEdgeAction */,
+ null /* swipeLeftFromEdgeAction */);
+
+ // Swipe Left from Edge
+ assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH, 1, 5, 1);
+ // Swipe Right from Edge
+ assertGestureTriggersAction(swipeRight, 0, 1, NAVBAR_WIDTH, 5);
+ }
+
private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL,
int navPos) {
mController.setBarState(isRTL, navPos);
@@ -577,7 +684,6 @@
reset(buttonView);
}
-
private MotionEvent event(int action, float x, float y) {
final MotionEvent event = mock(MotionEvent.class);
doReturn(x).when(event).getX();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 59a4937..02f8949 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -70,7 +70,7 @@
mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
statusBarWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
- mock(ActivityLaunchAnimator.Callback.class));
+ mock(ActivityLaunchAnimator.class), mock(StatusBarKeyguardViewManager.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e9e8eb7..c207fef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -72,6 +72,8 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -123,6 +125,7 @@
@Mock private IStatusBarService mBarService;
@Mock private IDreamManager mDreamManager;
@Mock private ScrimController mScrimController;
+ @Mock private DozeScrimController mDozeScrimController;
@Mock private ArrayList<Entry> mNotificationList;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
@@ -211,7 +214,7 @@
mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class),
mock(NotificationGroupAlertTransferHelper.class), mock(FalsingManager.class),
mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
- mock(DozeScrimController.class), mock(NotificationShelf.class),
+ mDozeScrimController, mock(NotificationShelf.class),
mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
mock(BubbleController.class));
mStatusBar.mContext = mContext;
@@ -570,7 +573,28 @@
}
@Test
+ public void testPulseWhileDozing_updatesScrimController() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mStatusBar.showKeyguardImpl();
+ // Keep track of callback to be able to stop the pulse
+ DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+ doAnswer(invocation -> {
+ pulseCallback[0] = invocation.getArgument(0);
+ return null;
+ }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ // Starting a pulse should change the scrim controller to the pulsing state
+ mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
+ DozeLog.PULSE_REASON_NOTIFICATION);
+ verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
+
+ // Ending a pulse should take it back to keyguard state
+ pulseCallback[0].onPulseFinished();
+ verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
+ }
+
+ @Test
public void testSetState_changesIsFullScreenUserSwitcherState() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
assertFalse(mStatusBar.isFullScreenUserSwitcherState());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index dcd531d..090963b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -15,6 +15,7 @@
package com.android.systemui.statusbar.phone;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -35,7 +36,7 @@
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
@SmallTest
public class SystemUIDialogTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
new file mode 100644
index 0000000..93c97ec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.Lifecycle.Event;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CallbackControllerTest extends SysuiTestCase {
+
+ @Test
+ public void testAddCallback() {
+ Lifecycle lifecycle = mock(Lifecycle.class);
+ LifecycleOwner owner = () -> lifecycle;
+ Object callback = new Object();
+ Controller controller = mock(Controller.class);
+
+ // observe and get the lifecycle observer that gets registered.
+ ArgumentCaptor<LifecycleEventObserver> observer =
+ ArgumentCaptor.forClass(LifecycleEventObserver.class);
+ controller.observe(owner, callback);
+ verify(lifecycle).addObserver(observer.capture());
+
+ // move to resume state and make sure the callback gets registered.
+ observer.getValue().onStateChanged(owner, Event.ON_RESUME);
+ verify(controller).addCallback(eq(callback));
+ }
+
+ @Test
+ public void testRemoveCallback() {
+ Lifecycle lifecycle = mock(Lifecycle.class);
+ LifecycleOwner owner = () -> lifecycle;
+ Object callback = new Object();
+ Controller controller = mock(Controller.class);
+
+ // observe and get the lifecycle observer that gets registered.
+ ArgumentCaptor<LifecycleEventObserver> observer =
+ ArgumentCaptor.forClass(LifecycleEventObserver.class);
+ controller.observe(owner, callback);
+ verify(lifecycle).addObserver(observer.capture());
+
+ // move to pause state and make sure the callback gets unregistered.
+ observer.getValue().onStateChanged(owner, Event.ON_PAUSE);
+ verify(controller).removeCallback(eq(callback));
+ }
+
+ private static class Controller implements CallbackController<Object> {
+ @Override
+ public void addCallback(Object listener) {
+ }
+
+ @Override
+ public void removeCallback(Object listener) {
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 3164c04..b3ac6be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -32,9 +32,10 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.util.Assert;
import org.junit.After;
import org.junit.Before;
@@ -44,7 +45,7 @@
import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
@SmallTest
public class RemoteInputViewTest extends SysuiTestCase {
@@ -60,6 +61,7 @@
@Before
public void setUp() throws Exception {
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index df7aeab..c5bac92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -98,6 +98,7 @@
private Notification mNotification;
@Mock ActivityStarter mActivityStarter;
+ @Mock HeadsUpManager mHeadsUpManager;
@Before
public void setUp() {
@@ -434,7 +435,8 @@
mView.addSmartActions(
new SmartReplyView.SmartActions(createActions(actionTitles), false),
mLogger,
- mEntry);
+ mEntry,
+ mHeadsUpManager);
}
private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
@@ -442,7 +444,8 @@
mView.addSmartActions(
new SmartReplyView.SmartActions(createActions(actionTitles), false),
mLogger,
- mEntry);
+ mEntry,
+ mHeadsUpManager);
}
private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -747,7 +750,7 @@
private Button inflateActionButton(Notification.Action action) {
return mView.inflateActionButton(getContext(), mView, 0,
new SmartReplyView.SmartActions(Collections.singletonList(action), false),
- mLogger, mEntry);
+ mLogger, mEntry, mHeadsUpManager);
}
@Test
@@ -801,4 +804,55 @@
assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
}
+
+ @Test
+ public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
+ String[] choices = new String[] {"Reply"};
+ String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new String[0], 2,
+ createActions(new String[] {
+ "Looooooong \nactioooon", "second \naction", "third \naction"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ // smart replies
+ assertReplyButtonHidden(mView.getChildAt(0));
+ // smart actions
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+ }
+
+ @Test
+ public void testMeasure_choicesAndActionsPrioritizeActions() {
+ String[] choices = new String[] {"Short", "longer reply"};
+ String[] actions = new String[] {"Looooooong actioooon", "second action"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
+ createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ Button firstAction = ((Button) mView.getChildAt(1));
+
+ assertEqualMeasures(expectedView, mView);
+ // smart replies
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonHidden(mView.getChildAt(1));
+ // smart actions
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
new file mode 100644
index 0000000..d4e38d8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
+import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.lifecycle.LifecycleEventObserver;
+
+import com.android.systemui.SysuiBaseFragmentTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LifecycleFragmentTest extends SysuiBaseFragmentTest {
+
+ public LifecycleFragmentTest() {
+ super(LifecycleFragment.class);
+ }
+
+ @Test
+ public void testCreateLifecycle() {
+ LifecycleFragment fragment = (LifecycleFragment) mFragment;
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ fragment.getLifecycle().addObserver(observer);
+
+ mFragments.dispatchCreate();
+ TestableLooper.get(this).processAllMessages();
+
+ verify(observer).onStateChanged(eq(fragment), eq(ON_CREATE));
+ }
+
+ @Test
+ public void testResumeLifecycle() {
+ LifecycleFragment fragment = (LifecycleFragment) mFragment;
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ fragment.getLifecycle().addObserver(observer);
+
+ mFragments.dispatchResume();
+ TestableLooper.get(this).processAllMessages();
+
+ verify(observer).onStateChanged(eq(fragment), eq(ON_RESUME));
+ }
+
+ @Test
+ public void testStartLifecycle() {
+ LifecycleFragment fragment = (LifecycleFragment) mFragment;
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ fragment.getLifecycle().addObserver(observer);
+
+ mFragments.dispatchStart();
+ TestableLooper.get(this).processAllMessages();
+
+ verify(observer).onStateChanged(eq(fragment), eq(ON_START));
+ }
+
+ @Test
+ public void testStopLifecycle() {
+ LifecycleFragment fragment = (LifecycleFragment) mFragment;
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ fragment.getLifecycle().addObserver(observer);
+
+ mFragments.dispatchStart();
+ TestableLooper.get(this).processAllMessages();
+ mFragments.dispatchStop();
+ TestableLooper.get(this).processAllMessages();
+
+ verify(observer).onStateChanged(eq(fragment), eq(ON_STOP));
+ }
+
+ @Test
+ public void testPauseLifecycle() {
+ LifecycleFragment fragment = (LifecycleFragment) mFragment;
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ fragment.getLifecycle().addObserver(observer);
+
+ mFragments.dispatchResume();
+ TestableLooper.get(this).processAllMessages();
+ mFragments.dispatchPause();
+ TestableLooper.get(this).processAllMessages();
+
+ verify(observer).onStateChanged(eq(fragment), eq(ON_PAUSE));
+ }
+
+ @Test
+ public void testDestroyLifecycle() {
+ LifecycleFragment fragment = (LifecycleFragment) mFragment;
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ fragment.getLifecycle().addObserver(observer);
+
+ mFragments.dispatchCreate();
+ TestableLooper.get(this).processAllMessages();
+ mFragments.dispatchDestroy();
+ TestableLooper.get(this).processAllMessages();
+ mFragments = null;
+
+ verify(observer).onStateChanged(eq(fragment), eq(ON_DESTROY));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
new file mode 100644
index 0000000..d63bbe6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
+import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
+import android.view.View;
+
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SysuiLifecycleTest extends SysuiTestCase {
+
+ @Test
+ public void testAttach() {
+ View v = new View(mContext);
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ LifecycleOwner lifecycle = viewAttachLifecycle(v);
+ lifecycle.getLifecycle().addObserver(observer);
+
+ ViewUtils.attachView(v);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(observer).onStateChanged(eq(lifecycle), eq(ON_CREATE));
+ verify(observer).onStateChanged(eq(lifecycle), eq(ON_START));
+ verify(observer).onStateChanged(eq(lifecycle), eq(ON_RESUME));
+
+ ViewUtils.detachView(v);
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ @Test
+ public void testDetach() {
+ View v = new View(mContext);
+ LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+ LifecycleOwner lifecycle = viewAttachLifecycle(v);
+ lifecycle.getLifecycle().addObserver(observer);
+
+ ViewUtils.attachView(v);
+ TestableLooper.get(this).processAllMessages();
+
+ ViewUtils.detachView(v);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(observer).onStateChanged(eq(lifecycle), eq(ON_PAUSE));
+ verify(observer).onStateChanged(eq(lifecycle), eq(ON_STOP));
+ verify(observer).onStateChanged(eq(lifecycle), eq(ON_DESTROY));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
index 43942f7..ab9b0c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
@@ -20,11 +20,13 @@
import static org.mockito.Mockito.verify;
import android.animation.Animator;
+import android.os.Looper;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -33,7 +35,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class KeepAwakeAnimationListenerTest extends SysuiTestCase {
@Mock WakeLock mWakeLock;
@@ -41,6 +43,7 @@
@Before
public void setup() {
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
KeepAwakeAnimationListener.sWakeLock = mWakeLock;
mKeepAwakeAnimationListener = new KeepAwakeAnimationListener(getContext());
@@ -55,4 +58,10 @@
mKeepAwakeAnimationListener.onAnimationEnd((Animator) null);
verify(mWakeLock).release();
}
+
+ @Test(expected = IllegalStateException.class)
+ public void initThrows_onNonMainThread() {
+ Assert.sMainLooper = Looper.getMainLooper();
+ new KeepAwakeAnimationListener(getContext());
+ }
}
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 1cb5602..372147f 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -32,5 +32,5 @@
<string name="configure" msgid="4905518375574791375">"Configurar"</string>
<string name="disconnect" msgid="971412338304200056">"Desconectar"</string>
<string name="open_app" msgid="3717639178595958667">"Abrir aplicación"</string>
- <string name="dismiss" msgid="6192859333764711227">"Ignorar"</string>
+ <string name="dismiss" msgid="6192859333764711227">"Cerrar"</string>
</resources>
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
new file mode 100644
index 0000000..d316fbd
--- /dev/null
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := AccentColorBlack
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AccentColorBlackOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..3b99648
--- /dev/null
+++ b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.color.black"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/>
+
+ <application android:label="@string/accent_color_black_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml
similarity index 75%
copy from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
copy to packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml
index 23d607d..5648f91 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -13,7 +15,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.service.intelligence;
-
-parcelable ContentCaptureEventsRequest;
+-->
+<resources>
+ <color name="accent_device_default_light">#202020</color>
+ <color name="accent_device_default_dark">#FFFFFF</color>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
similarity index 68%
copy from packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
copy to packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
index d5259d3..baf09b1 100644
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -15,9 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"精簡導覽列實驗"</string>
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Black accent color name application label. [CHAR LIMIT=50] -->
+ <string name="accent_color_black_overlay" translatable="false">Black Accent Color</string>
</resources>
+
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
new file mode 100644
index 0000000..afc4287
--- /dev/null
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := AccentColorGreen
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AccentColorGreenOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..609d5be
--- /dev/null
+++ b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.color.green"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/>
+
+ <application android:label="@string/accent_color_green_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml
similarity index 75%
copy from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
copy to packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml
index 23d607d..089f08c 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -13,7 +15,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.service.intelligence;
-
-parcelable ContentCaptureEventsRequest;
+-->
+<resources>
+ <color name="accent_device_default_light">#1B873B</color>
+ <color name="accent_device_default_dark">#84C188</color>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
similarity index 68%
copy from packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
copy to packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
index d5259d3..4de344c 100644
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -15,9 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"精簡導覽列實驗"</string>
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Green accent color name application label. [CHAR LIMIT=50] -->
+ <string name="accent_color_green_overlay" translatable="false">Green Accent Color</string>
</resources>
+
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
new file mode 100644
index 0000000..3366169
--- /dev/null
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := AccentColorPurple
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AccentColorPurpleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..497a358
--- /dev/null
+++ b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.color.purple"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/>
+
+ <application android:label="@string/accent_color_purple_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml
similarity index 75%
copy from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
copy to packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml
index 23d607d..7e34bac 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -13,7 +15,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.service.intelligence;
-
-parcelable ContentCaptureEventsRequest;
+-->
+<resources>
+ <color name="accent_device_default_light">#725AFF</color>
+ <color name="accent_device_default_dark">#B5A9FC</color>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
similarity index 68%
rename from packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
rename to packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
index d5259d3..d1eb95a 100644
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -15,9 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"精簡導覽列實驗"</string>
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Purple accent color name application label. [CHAR LIMIT=50] -->
+ <string name="accent_color_purple_overlay" translatable="false">Purple Accent Color</string>
</resources>
+
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values-en-rXC/strings.xml
similarity index 65%
rename from packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
rename to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values-en-rXC/strings.xml
index 6a1ce41..c5ed415 100644
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values-en-rXC/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Úzky navigačný panel (experiment)"</string>
+ <string name="experiment_navigationbar_overlay" msgid="3475628315769912732">"Default Navigation Bar Experiment (48dp)"</string>
</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml
deleted file mode 100644
index 21a0003..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment met dun navigasiebalk"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml
deleted file mode 100644
index 6a7d644..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ቀጭን የአሰሳ አሞሌ ሙከራ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml
deleted file mode 100644
index 2cebf40..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"تجربة شريط التنقُّل النحيف"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml
deleted file mode 100644
index 266c051..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Sabit Naviqasiya Paneli Təcrübəsi"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index dc57a3f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment sa tankom trakom za navigaciju"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml
deleted file mode 100644
index 8b53bcd..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Эксперымент з тонкай панэллю навігацыі"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml
deleted file mode 100644
index 4bf000e..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент с тънка лента за навигация"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml
deleted file mode 100644
index 9814209..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment s tankom trakom za navigaciju"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml
deleted file mode 100644
index 4a9b598..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment amb barra de navegació fina"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml
deleted file mode 100644
index d923a3c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Úzký navigační panel (experiment)"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml
deleted file mode 100644
index 12bb2f2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Test med smal navigationslinje"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml
deleted file mode 100644
index 960a7d9..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment mit schmaler Navigationsleiste"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml
deleted file mode 100644
index 119b7e6..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Πείραμα λεπτής γραμμής πλοήγησης"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
index 79884ce..40d9fbc 100644
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
+ <string name="experiment_navigationbar_overlay" msgid="9207872199884142345">"Slim Navigation Bar Experiment (24dp)"</string>
</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml
deleted file mode 100644
index 00b0444..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegación delgada"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml
deleted file mode 100644
index 58bdaa2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra navegación delgada"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml
deleted file mode 100644
index 4c5023c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Kitsa navigeerimisriba katse"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml
deleted file mode 100644
index fc16eeb..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Nabigazio-barra finaren esperimentua"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml
deleted file mode 100644
index e4246d7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"آزمایش نوار پیمایش باریک"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml
deleted file mode 100644
index 9385a47..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Ohuen navigointipalkin kokeilu"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml
deleted file mode 100644
index d22848b..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Expérience de barre de navigation mince"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml
deleted file mode 100644
index cfa1db2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Test relatif à la barre de navigation fine"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml
deleted file mode 100644
index d2194ce..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegación estreita"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml
deleted file mode 100644
index d51cee3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"स्लिम नेविगेशन बार प्रयोग"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml
deleted file mode 100644
index 410c58e..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment s tankom navigacijskom trakom"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml
deleted file mode 100644
index d7eafed..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Kísérleti keskeny navigációs sáv"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml
deleted file mode 100644
index b0200ca..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Նավարկման նեղ գոտու փորձարկում"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml
deleted file mode 100644
index 3e0bded..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperimen Menu Navigasi Ramping"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml
deleted file mode 100644
index 03ccaf3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Tilraun með þunna yfirlitsstiku"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml
deleted file mode 100644
index f7c5d253..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Esperimento Barra di navigazione sottile"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml
deleted file mode 100644
index 0d0ec2c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ניסוי של סרגל ניווט דק"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml
deleted file mode 100644
index a3d6874..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"スリム ナビゲーション バー テスト"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml
deleted file mode 100644
index ffddf3b..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ნავიგაციის მჭიდრო ზოლის ექსპერიმენტი"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml
deleted file mode 100644
index f34ac08..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Жіңішке навигация жолағы (эксперимент)"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml
deleted file mode 100644
index 114a782..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ការពិសោធនៃរបាររុករកស្ដើង"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml
deleted file mode 100644
index fca02c3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"슬림한 탐색 메뉴 실험"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml
deleted file mode 100644
index 449de4f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Чакан чабыттоо тилкесин сыноо"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml
deleted file mode 100644
index 6ec48ca..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ການທົດລອງແຖບການນຳທາງແບບບາງ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml
deleted file mode 100644
index 1df54aa..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Plonos naršymo juostos eksperimentas"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml
deleted file mode 100644
index 5c6c565..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Plānas navigācijas joslas eksperiments"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml
deleted file mode 100644
index 3517813..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент со тенка лента за навигација"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml
deleted file mode 100644
index a2282c4..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Нимгэн навигацийн самбарын туршилт"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml
deleted file mode 100644
index c714370..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"स्लिम नॅव्हिगेशन बार प्रयोग"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml
deleted file mode 100644
index 68f831e..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Percubaan Bar Navigasi Langsing"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml
deleted file mode 100644
index 84db279..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ပါးလွှာသော လမ်းညွှန်ဘား စမ်းသပ်မှု"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml
deleted file mode 100644
index e1ff863..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment med tynn navigasjonsrad"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml
deleted file mode 100644
index 01190bc..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment voor smalle navigatiebalk"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml
deleted file mode 100644
index 1742aad..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperyment z wąskim paskiem nawigacyjnym"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 22194b7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegação fina"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml
deleted file mode 100644
index f6c0309..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiência de barra de navegação fina"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml
deleted file mode 100644
index 22194b7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegação fina"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml
deleted file mode 100644
index e1655f2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment cu bară de navigare subțire"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml
deleted file mode 100644
index cac66dc..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Узкая панель навигации (эксперимент)"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml
deleted file mode 100644
index beab7b6..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Preizkus z vitko vrstico za krmarjenje"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml
deleted file mode 100644
index b7a28d5..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperimenti i shiritit të hollë të navigimit"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml
deleted file mode 100644
index 048f649..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент са танком траком за навигацију"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml
deleted file mode 100644
index b94438f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimentellt tunt navigeringsfält"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml
deleted file mode 100644
index 3a5a73c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Jaribio la Sehemu ya Viungo Muhimu Inayoweza Kupunguzwa"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml
deleted file mode 100644
index 945297b..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"การทดสอบแถบนำทางแบบบาง"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml
deleted file mode 100644
index 0c8087c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperimentong Slim na Navigation Bar"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml
deleted file mode 100644
index a3ca754..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"İnce Gezinme Çubuğu Denemesi"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml
deleted file mode 100644
index 656c4a9..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент із тонкою панеллю навігації"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml
deleted file mode 100644
index bcd6bc3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"سلم نیویگیشن بار کا تجربہ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml
deleted file mode 100644
index 0d40981..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Ingichka navigatsiya paneli tajribasi"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml
deleted file mode 100644
index dad56b4..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Thử nghiệm thanh điều hướng mỏng"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml
deleted file mode 100644
index b2602e0..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"精简导航栏实验"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 6586a57..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"細長導覽列實驗"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml
deleted file mode 100644
index 12d04d3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Ukuhlolwa kwebha yokuzula encane"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values-en-rXC/strings.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values-en-rXC/strings.xml
index 6a1ce41..ca8c4fa 100644
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values-en-rXC/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Úzky navigačný panel (experiment)"</string>
+ <string name="experiment_navigationbar_overlay" msgid="2616250866244714325">"Slim Navigation Bar Experiment (32dp)"</string>
</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values-en-rXC/strings.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values-en-rXC/strings.xml
index 6a1ce41..9209c2b 100644
--- a/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values-en-rXC/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Úzky navigačný panel (experiment)"</string>
+ <string name="experiment_navigationbar_overlay" msgid="6415947279345789008">"Slim Navigation Bar Experiment (40dp)"</string>
</resources>
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
new file mode 100644
index 0000000..a734a6b
--- /dev/null
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeRoundedRect
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeRoundedRectOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml b/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..39c082b
--- /dev/null
+++ b/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon.roundedrect"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="android.theme.customization.adaptive_icon_shape"
+ android:priority="1"/>
+
+ <application android:label="@string/icon_shape_roundedrect_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
new file mode 100644
index 0000000..f024615
--- /dev/null
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
similarity index 68%
copy from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
copy to packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
index 23d607d..dc5c196 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -13,7 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Rounded corner rectangle overlay -->
+ <string name="icon_shape_roundedrect_overlay" translatable="false">RoundedRectangle Icons</string>
-package android.service.intelligence;
-
-parcelable ContentCaptureEventsRequest;
+</resources>
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
new file mode 100644
index 0000000..217da9f
--- /dev/null
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeSquare
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeSquareOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml b/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..235fdeb
--- /dev/null
+++ b/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon.square"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="android.theme.customization.adaptive_icon_shape"
+ android:priority="1"/>
+
+ <application android:label="@string/icon_shape_square_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/config.xml b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml
new file mode 100644
index 0000000..54623f5
--- /dev/null
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"M50,0L100,0 100,100 0,100 0,0z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
similarity index 70%
copy from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
copy to packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
index 23d607d..4fd39ff26 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -13,7 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Square icon overlay -->
+ <string name="icon_shape_square_overlay" translatable="false">Square Icons</string>
-package android.service.intelligence;
-
-parcelable ContentCaptureEventsRequest;
+</resources>
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
new file mode 100644
index 0000000..fd3bfa0
--- /dev/null
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeSquircle
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeSquircleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml b/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..ca618e4
--- /dev/null
+++ b/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon.squircle"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="android.theme.customization.adaptive_icon_shape"
+ android:priority="1"/>
+
+ <application android:label="@string/icon_shape_squircle_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeSquircleOverlay/res/values/config.xml b/packages/overlays/IconShapeSquircleOverlay/res/values/config.xml
new file mode 100644
index 0000000..eaf7de3
--- /dev/null
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
similarity index 70%
copy from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
copy to packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
index 23d607d..b7c001c 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -13,7 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Squircle icon shape overlay -->
+ <string name="icon_shape_squircle_overlay" translatable="false">Square Icons</string>
-package android.service.intelligence;
-
-parcelable ContentCaptureEventsRequest;
+</resources>
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
new file mode 100644
index 0000000..ea43423
--- /dev/null
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeTeardrop
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeTeardropOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml b/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..b7d5ecb
--- /dev/null
+++ b/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon.teardrop"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="android.theme.customization.adaptive_icon_shape"
+ android:priority="1"/>
+
+ <application android:label="@string/icon_shape_teardrop_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
new file mode 100644
index 0000000..43ad04d
--- /dev/null
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
similarity index 70%
copy from core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
copy to packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
index 23d607d..d946ee8 100644
--- a/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -13,7 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Teardrop icon overlay -->
+ <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop Icons</string>
-package android.service.intelligence;
-
-parcelable ContentCaptureEventsRequest;
+</resources>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3e07d12..2d30640 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6604,6 +6604,39 @@
// OS: Q
NOTIFICATION_DIRECT_REPLY_ACTION = 1590;
+ // OPEN: Settings > Developer options > Disable > Info dialog
+ // CATEGORY: SETTINGS
+ // OS: Q
+ DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
+
+ // Tag for an ACTION: QS -> Tile click / Secondary click / long press
+ // indicating the StatusBarState when menu was pulled down
+ // CATEGORY: QUICK_SETTINGS
+ // OS: Q
+ FIELD_STATUS_BAR_STATE = 1592;
+
+ // Tag for an ACTION: QS -> Tile click / Secondary click / long press
+ // indicating whether current state is full QS
+ // CATEGORY: QUICK_SETTINGS
+ // OS: Q
+ FIELD_IS_FULL_QS = 1593;
+
+ // ACTION: Display folding state was changed
+ // CATEGORY: OTHER
+ // SUBTYPE: 1 if display is folded, 0 if not.
+ // OS: Q
+ ACTION_DISPLAY_FOLD = 1594;
+
+ // OPEN: WifiDppConfiguratorActivity (android.settings.WIFI_DPP_CONFIGURATOR_XXX action intents)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_WIFI_DPP_CONFIGURATOR = 1595;
+
+ // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_WIFI_DPP_ENROLLEE = 1596;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index fba639c..1212676 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -216,6 +216,10 @@
// Package: android
NOTE_SOFTAP_CONFIG_CHANGED = 50;
+ // Notify the user that connected to app suggested network.
+ // Package: android
+ NOTE_CONNECTED_TO_NETWORK_SUGGESTION = 51;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 1fda074..e9ce737 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1344,6 +1344,27 @@
// Amount of time wifi is in tx (ms)
optional int64 tx_time_ms = 5;
+
+ // Amount of time kernel is active because of wifi data (ms)
+ optional int64 wifi_kernel_active_time_ms = 6;
+
+ // Number of packets sent (tx)
+ optional int64 num_packets_tx = 7;
+
+ // Number of bytes sent (tx)
+ optional int64 num_bytes_tx = 8;
+
+ // Number of packets received (rx)
+ optional int64 num_packets_rx = 9;
+
+ // Number of bytes sent (rx)
+ optional int64 num_bytes_rx = 10;
+
+ // Amount of time wifi is in sleep (ms)
+ optional int64 sleep_time_ms = 11;
+
+ // Amount of time wifi is scanning (ms)
+ optional int64 scan_time_ms = 12;
}
// Metrics for Wifi Wake
diff --git a/services/Android.bp b/services/Android.bp
index 3390438..58a0997 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -21,9 +21,9 @@
"services.autofill",
"services.backup",
"services.companion",
+ "services.contentcapture",
"services.coverage",
"services.devicepolicy",
- "services.intelligence",
"services.midi",
"services.net",
"services.print",
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d5decce..36ca52a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2466,6 +2466,25 @@
}
/**
+ * AIDL-exposed method. System only.
+ * Gets accessibility window id from window token.
+ *
+ * @param windowToken Window token to get accessibility window id.
+ * @return Accessibility window id for the window token. Returns -1 if no such token is
+ * registered.
+ */
+ @Override
+ public int getAccessibilityWindowId(IBinder windowToken) {
+ synchronized (mLock) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+ throw new SecurityException("Only SYSTEM can call getAccessibilityWindowId");
+ }
+
+ return findWindowIdLocked(windowToken);
+ }
+ }
+
+ /**
* Get the recommended timeout of interactive controls and non-interactive controls.
*
* @return A long for pair of {@code int}s. First integer for interactive one, and second
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c56f31e..612c929 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -70,11 +70,10 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
-import com.android.server.AbstractMasterSystemService;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.autofill.ui.AutoFillUI;
-import com.android.server.intelligence.IntelligenceManagerInternal;
+import com.android.server.infra.AbstractMasterSystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -100,6 +99,7 @@
private static final Object sLock = AutofillManagerService.class;
+ private static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
/**
* IME supports Smart Suggestions.
@@ -155,7 +155,7 @@
private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
private final LocalService mLocalService = new LocalService();
- final IntelligenceManagerInternal mIntelligenceManagerInternal;
+ private final ActivityManagerInternal mAm;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -175,10 +175,6 @@
}
};
- // TODO(b/117779333): move to superclass / create super-class for ShellCommand
- @GuardedBy("mLock")
- private boolean mAllowInstantService;
-
/**
* Supported modes for Augmented Autofill Smart Suggestions.
*/
@@ -188,7 +184,7 @@
public AutofillManagerService(Context context) {
super(context, UserManager.DISALLOW_AUTOFILL);
mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
- mIntelligenceManagerInternal = LocalServices.getService(IntelligenceManagerInternal.class);
+ mAm = LocalServices.getService(ActivityManagerInternal.class);
setLogLevelFromSettings();
setMaxPartitionsFromSettings();
@@ -271,6 +267,11 @@
addCompatibilityModeRequestsLocked(service, userId);
}
+ @Override // from AbstractMasterSystemService
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ }
+
@Override // from SystemService
public void onStart() {
publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
@@ -287,10 +288,15 @@
return mSupportedSmartSuggestionModes;
}
+ // Called by AutofillManagerServiceImpl, doesn't need to check permission
+ boolean isInstantServiceAllowed() {
+ return mAllowInstantService;
+ }
+
// Called by Shell command.
void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
if (userId != UserHandle.USER_ALL) {
@@ -313,7 +319,7 @@
// Called by Shell command.
void listSessions(int userId, IResultReceiver receiver) {
Slog.i(TAG, "listSessions() for userId " + userId);
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
final Bundle resultData = new Bundle();
final ArrayList<String> sessions = new ArrayList<>();
@@ -340,7 +346,7 @@
// Called by Shell command.
void reset() {
Slog.i(TAG, "reset()");
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
visitServicesLocked((s) -> s.destroyLocked());
@@ -351,7 +357,7 @@
// Called by Shell command.
void setLogLevel(int level) {
Slog.i(TAG, "setLogLevel(): " + level);
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -388,7 +394,7 @@
// Called by Shell command.
int getLogLevel() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -399,7 +405,7 @@
// Called by Shell command.
int getMaxPartitions() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
return sPartitionMaxCount;
@@ -408,8 +414,8 @@
// Called by Shell command.
void setMaxPartitions(int max) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxPartitions(): " + max);
+ enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -433,7 +439,7 @@
// Called by Shell command.
int getMaxVisibleDatasets() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (sLock) {
return sVisibleDatasetsMaxCount;
@@ -442,8 +448,8 @@
// Called by Shell command.
void setMaxVisibleDatasets(int max) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
+ enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -478,43 +484,59 @@
}
// Called by Shell command.
- void getScore(@Nullable String algorithmName, @NonNull String value1,
+ void calculateScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
final FieldClassificationStrategy strategy =
new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
- strategy.getScores(callback, algorithmName, null,
- Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 });
+ strategy.calculateScores(callback, Arrays.asList(AutofillValue.forText(value1)),
+ new String[] { value2 }, null, algorithmName, null, null, null);
}
// Called by Shell command.
Boolean getFullScreenMode() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
return sFullScreenMode;
}
// Called by Shell command.
void setFullScreenMode(@Nullable Boolean mode) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
sFullScreenMode = mode;
}
// Called by Shell command.
- boolean getAllowInstantService() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ void setTemporaryAugmentedAutofillService(@UserIdInt int userId, @NonNull String serviceName,
+ int durationMs) {
+ Slog.i(mTag, "setTemporaryAugmentedAutofillService(" + userId + ") to " + serviceName
+ + " for " + durationMs + "ms");
+ enforceCallingPermissionForManagement();
+
+ Preconditions.checkNotNull(serviceName);
+ if (durationMs > MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS) {
+ throw new IllegalArgumentException("Max duration is "
+ + MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS + " (called with " + durationMs + ")");
+ }
+
synchronized (mLock) {
- return mAllowInstantService;
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.mAugmentedAutofillResolver.setTemporaryServiceLocked(serviceName,
+ durationMs);
+ }
}
}
- // Called by Shell command.
- void setAllowInstantService(boolean mode) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
- Slog.i(TAG, "setAllowInstantService(): " + mode);
+ // Called by Shell command
+ void resetTemporaryAugmentedAutofillService(@UserIdInt int userId) {
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
- mAllowInstantService = mode;
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.mAugmentedAutofillResolver.resetTemporaryServiceLocked();
+ }
}
}
@@ -893,14 +915,9 @@
throw new IllegalArgumentException(packageName + " is not a valid package", e);
}
- // TODO(b/113281366): rather than always call AM here, call it on demand on
- // getPreviousSessionsLocked()? That way we save space / time here, and don't set
- // a callback on AM unnecessarily (see TODO below :-)
- final ActivityManagerInternal am = LocalServices
- .getService(ActivityManagerInternal.class);
// TODO(b/113281366): add a callback method on AM to be notified when a task is finished
// so we can clean up sessions kept alive
- final int taskId = am.getTaskIdForActivity(activityToken, false);
+ final int taskId = mAm.getTaskIdForActivity(activityToken, false);
final int sessionId;
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
@@ -1218,7 +1235,6 @@
mAutofillCompatState.dump(prefix, pw);
pw.print("from settings: ");
pw.println(getWhitelistedCompatModePackagesFromSettings());
- pw.print("Allow instant service: "); pw.println(mAllowInstantService);
if (mSupportedSmartSuggestionModes != 0) {
pw.print("Smart Suggestion modes: ");
pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0df99d4..fa62ef8 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -70,11 +70,14 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.server.AbstractPerUserSystemService;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
+import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.infra.SecureSettingsServiceNameResolver;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -151,10 +154,24 @@
/** When was {@link PruneTask} last executed? */
private long mLastPrune = 0;
+ /**
+ * Object used to set the name of the augmented autofill service.
+ */
+ @NonNull
+ final FrameworkResourcesServiceNameResolver mAugmentedAutofillResolver;
+
+ /**
+ * Reference to the {@link RemoteAugmentedAutofillService}, is set on demand.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteAugmentedAutofillService mRemoteAugmentedAutofillService;
+
AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState, boolean disabled) {
- super(master, lock, userId);
+ super(master, new SecureSettingsServiceNameResolver(master.getContext(), userId,
+ Settings.Secure.AUTOFILL_SERVICE), lock, userId);
mRequestsHistory = requestsHistory;
mUiLatencyHistory = uiLatencyHistory;
@@ -162,6 +179,12 @@
mUi = ui;
mFieldClassificationStrategy = new FieldClassificationStrategy(getContext(), userId);
mAutofillCompatState = autofillCompatState;
+
+ mAugmentedAutofillResolver = new FrameworkResourcesServiceNameResolver(master.getContext(),
+ userId, lock, com.android.internal.R.string.config_defaultAugmentedAutofillService);
+ mAugmentedAutofillResolver.setOnTemporaryServiceNameChangedCallback(
+ () -> updateRemoteAugmentedAutofillService());
+
updateLocked(disabled);
}
@@ -369,7 +392,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- final String autoFillService = getComponentNameFromSettings();
+ final String autoFillService = getComponentNameLocked();
final ComponentName componentName = serviceInfo.getComponentName();
if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
@@ -847,10 +870,14 @@
pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
}
- pw.print(prefix); pw.print("Component from settings: ");
- pw.println(getComponentNameFromSettings());
pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
.getString(R.string.config_defaultAutofillService));
+ pw.print(prefix); pw.print("mAugmentedAutofillNamer: ");
+ mAugmentedAutofillResolver.dumpShortLocked(pw); pw.println();
+ if (mRemoteAugmentedAutofillService != null) {
+ pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
+ mRemoteAugmentedAutofillService.dump(prefix2, pw);
+ }
pw.print(prefix); pw.print("Field classification enabled: ");
pw.println(isFieldClassificationEnabledLocked());
pw.print(prefix); pw.print("Compat pkgs: ");
@@ -992,6 +1019,50 @@
return null;
}
+ @GuardedBy("mLock")
+ @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
+ if (mRemoteAugmentedAutofillService == null) {
+ final String serviceName = mAugmentedAutofillResolver.getServiceNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): not set");
+ }
+ return null;
+ }
+ final ComponentName componentName = RemoteAugmentedAutofillService.getComponentName(
+ serviceName, mUserId, mAugmentedAutofillResolver.isTemporaryLocked());
+ if (componentName == null) return null;
+ if (sVerbose) {
+ Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
+ }
+
+ mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
+ componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
+ @Override
+ public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
+ // TODO(b/111330312): properly implement
+ Slog.w(TAG, "remote augmented autofill service died");
+ }
+ }, mMaster.isInstantServiceAllowed(), mMaster.verbose);
+ }
+
+ return mRemoteAugmentedAutofillService;
+ }
+
+ /**
+ * Called when the {@link #mAugmentedAutofillResolver} changed (among other places).
+ */
+ private void updateRemoteAugmentedAutofillService() {
+ final String serviceName = mAugmentedAutofillResolver.getServiceNameLocked();
+ if (serviceName == null) {
+ if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!");
+ if (mRemoteAugmentedAutofillService != null) {
+ mRemoteAugmentedAutofillService.destroy();
+ mRemoteAugmentedAutofillService = null;
+ }
+ }
+ }
+
private void sendStateToClients(boolean resetClient) {
final RemoteCallbackList<IAutoFillManagerClient> clients;
final int userClientCount;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 522280e..c562fb1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -104,6 +104,11 @@
pw.println(" set bind-instant-service-allowed [true | false]");
pw.println(" Sets whether binding to services provided by instant apps is allowed");
pw.println("");
+ pw.println(" set temporary-augmented-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the augmented autofill service "
+ + "implementation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
pw.println(" list sessions [--user USER_ID]");
pw.println(" Lists all pending sessions.");
pw.println("");
@@ -151,6 +156,8 @@
return setFullScreenMode(pw);
case "bind-instant-service-allowed":
return setBindInstantService(pw);
+ case "temporary-augmented-service":
+ return setTemporaryAugmentedService(pw);
default:
pw.println("Invalid set: " + what);
return -1;
@@ -226,7 +233,7 @@
final String value2 = getNextArgRequired();
final CountDownLatch latch = new CountDownLatch(1);
- mService.getScore(algorithm, value1, value2, new RemoteCallback((result) -> {
+ mService.calculateScore(algorithm, value1, value2, new RemoteCallback((result) -> {
final Scores scores = result.getParcelable(EXTRA_SCORES);
if (scores == null) {
pw.println("no score");
@@ -293,6 +300,20 @@
}
}
+ private int setTemporaryAugmentedService(PrintWriter pw) {
+ final int userId = getNextIntArgRequired();
+ final String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryAugmentedAutofillService(userId);
+ return 0;
+ }
+ final int duration = getNextIntArgRequired();
+ mService.setTemporaryAugmentedAutofillService(userId, serviceName, duration);
+ pw.println("AugmentedAutofillService temporarily set to " + serviceName + " for "
+ + duration + "ms");
+ return 0;
+ }
+
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
@@ -371,4 +392,8 @@
}
return UserHandle.USER_ALL;
}
+
+ private int getNextIntArgRequired() {
+ return Integer.parseInt(getNextArgRequired());
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index 293f908e..9db6254 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -15,11 +15,12 @@
*/
package com.android.server.autofill;
-import static com.android.server.autofill.Helper.sDebug;
-import static com.android.server.autofill.Helper.sVerbose;
import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM;
+import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
+
import android.Manifest;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -40,6 +41,7 @@
import android.os.UserHandle;
import android.service.autofill.AutofillFieldClassificationService;
import android.service.autofill.IAutofillFieldClassificationService;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.view.autofill.AutofillValue;
@@ -257,13 +259,13 @@
return parser.get(res, resourceId);
}
- //TODO(b/70291841): rename this method (and all others in the chain) to something like
- // calculateScores() ?
- void getScores(RemoteCallback callback, @Nullable String algorithmName,
- @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues,
- @NonNull String[] userDataValues) {
- connectAndRun((service) -> service.getScores(callback, algorithmName,
- algorithmArgs, actualValues, userDataValues));
+ void calculateScores(RemoteCallback callback, @NonNull List<AutofillValue> actualValues,
+ @NonNull String[] userDataValues, @NonNull String[] categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable ArrayMap<String, String> algorithms,
+ @Nullable ArrayMap<String, Bundle> args) {
+ connectAndRun((service) -> service.calculateScores(callback, actualValues,
+ userDataValues, categoryIds, defaultAlgorithm, defaultArgs, algorithms, args));
}
void dump(String prefix, PrintWriter pw) {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
new file mode 100644
index 0000000..fc7265d
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.service.autofill.augmented.AugmentedAutofillService;
+import android.service.autofill.augmented.IAugmentedAutofillService;
+import android.service.autofill.augmented.IFillCallback;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillValue;
+import android.view.autofill.IAutoFillManagerClient;
+
+import com.android.internal.os.IResultReceiver;
+import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
+
+final class RemoteAugmentedAutofillService
+ extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService,
+ IAugmentedAutofillService> {
+
+ private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
+
+ // TODO(b/117779333): changed it so it's permanentely bound
+ private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+ RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
+ int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
+ boolean bindInstantServiceAllowed, boolean verbose) {
+ super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks,
+ bindInstantServiceAllowed, verbose);
+ }
+
+ @Nullable
+ public static ComponentName getComponentName(@NonNull String componentName,
+ @UserIdInt int userId, boolean isTemporary) {
+ int flags = PackageManager.GET_META_DATA;
+ if (!isTemporary) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
+ final ComponentName serviceComponent;
+ ServiceInfo serviceInfo = null;
+ try {
+ serviceComponent = ComponentName.unflattenFromString(componentName);
+ serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags,
+ userId);
+ if (serviceInfo == null) {
+ Slog.e(TAG, "Bad service name for flags " + flags + ": " + componentName);
+ return null;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
+ return null;
+ }
+ return serviceComponent;
+ }
+
+ @Override // from AbstractRemoteService
+ protected IAugmentedAutofillService getServiceInterface(IBinder service) {
+ return IAugmentedAutofillService.Stub.asInterface(service);
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getTimeoutIdleBindMillis() {
+ return TIMEOUT_IDLE_BIND_MILLIS;
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getRemoteRequestMillis() {
+ return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ }
+
+ /**
+ * Called by {@link Session} to request augmented autofill.
+ */
+ public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client,
+ int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
+ @Nullable AutofillValue focusedValue) {
+ scheduleRequest(new PendingAutofillRequest(this, sessionId, client, taskId,
+ activityComponent, focusedId, focusedValue));
+ }
+
+ /**
+ * Called by {@link Session} when it's time to destroy all augmented autofill requests.
+ */
+ public void onDestroyAutofillWindowsRequest(int sessionId) {
+ scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId));
+ }
+
+ // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass
+ private abstract static class MyPendingRequest
+ extends PendingRequest<RemoteAugmentedAutofillService, IAugmentedAutofillService> {
+ protected final int mSessionId;
+
+ private MyPendingRequest(@NonNull RemoteAugmentedAutofillService service, int sessionId) {
+ super(service);
+ mSessionId = sessionId;
+ }
+ }
+
+ private static final class PendingAutofillRequest extends MyPendingRequest {
+ private final @NonNull AutofillId mFocusedId;
+ private final @Nullable AutofillValue mFocusedValue;
+ private final @NonNull IAutoFillManagerClient mClient;
+ private final @NonNull ComponentName mActivityComponent;
+ private final int mTaskId;
+ private final long mRequestTime = SystemClock.elapsedRealtime();
+ private final @NonNull IFillCallback mCallback;
+
+ protected PendingAutofillRequest(@NonNull RemoteAugmentedAutofillService service,
+ int sessionId, @NonNull IAutoFillManagerClient client, int taskId,
+ @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
+ @Nullable AutofillValue focusedValue) {
+ super(service, sessionId);
+ mClient = client;
+ mTaskId = taskId;
+ mActivityComponent = activityComponent;
+ mFocusedId = focusedId;
+ mFocusedValue = focusedValue;
+ mCallback = new IFillCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ if (!finish()) return;
+ // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks
+ }
+ };
+ }
+
+ @Override
+ public void run() {
+ final RemoteAugmentedAutofillService remoteService = getService();
+ if (remoteService == null) return;
+
+ final IResultReceiver receiver = new IResultReceiver.Stub() {
+
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ final IBinder realClient = resultData
+ .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
+ remoteService.mService.onFillRequest(mSessionId, realClient, mTaskId,
+ mActivityComponent, mFocusedId, mFocusedValue, mRequestTime, mCallback);
+ }
+ };
+
+ // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
+ // cache IAugmentedAutofillManagerClient reference, etc...
+ try {
+ mClient.getAugmentedAutofillClient(receiver);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "exception handling getAugmentedAutofillClient() for "
+ + mSessionId + ": " + e);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
+ Slog.wtf(TAG, "timed out: " + this);
+ // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks
+ finish();
+ }
+
+ }
+
+ public interface RemoteAugmentedAutofillServiceCallbacks
+ extends VultureCallback<RemoteAugmentedAutofillService> {
+ // NOTE: so far we don't need to notify the callback implementation (an inner class on
+ // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
+ // callback interface is empty.
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index af65759..417ea9c 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -28,7 +28,6 @@
import android.content.IntentSender;
import android.os.IBinder;
import android.os.ICancellationSignal;
-import android.os.IInterface;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.FillRequest;
@@ -40,17 +39,17 @@
import android.text.format.DateUtils;
import android.util.Slog;
-import com.android.server.AbstractSinglePendingRequestRemoteService;
+import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
-final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> {
+final class RemoteFillService
+ extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> {
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private final FillServiceCallbacks mCallbacks;
- private IAutoFillService mAutoFillService;
- public interface FillServiceCallbacks extends VultureCallback {
+ public interface FillServiceCallbacks extends VultureCallback<RemoteFillService> {
void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@NonNull String servicePackageName, int requestFlags);
void onFillRequestFailure(int requestId, @Nullable CharSequence message);
@@ -71,21 +70,20 @@
@Override // from AbstractRemoteService
protected void handleOnConnectedStateChanged(boolean state) {
- if (mAutoFillService == null) {
+ if (mService == null) {
Slog.w(mTag, "onConnectedStateChanged(): null service");
return;
}
try {
- mAutoFillService.onConnectedStateChanged(state);
+ mService.onConnectedStateChanged(state);
} catch (Exception e) {
Slog.w(mTag, "Exception calling onConnectedStateChanged(): " + e);
}
}
@Override // from AbstractRemoteService
- protected IInterface getServiceInterface(IBinder service) {
- mAutoFillService = IAutoFillService.Stub.asInterface(service);
- return mAutoFillService;
+ protected IAutoFillService getServiceInterface(IBinder service) {
+ return IAutoFillService.Stub.asInterface(service);
}
@Override // from AbstractRemoteService
@@ -127,17 +125,15 @@
}
public void onFillRequest(@NonNull FillRequest request) {
- cancelScheduledUnbind();
scheduleRequest(new PendingFillRequest(request, this));
}
public void onSaveRequest(@NonNull SaveRequest request) {
- cancelScheduledUnbind();
scheduleRequest(new PendingSaveRequest(request, this));
}
private boolean handleResponseCallbackCommon(
- @NonNull PendingRequest<RemoteFillService> pendingRequest) {
+ @NonNull PendingRequest<RemoteFillService, IAutoFillService> pendingRequest) {
if (isDestroyed()) return false;
if (mPendingRequest == pendingRequest) {
@@ -204,7 +200,8 @@
});
}
- private static final class PendingFillRequest extends PendingRequest<RemoteFillService> {
+ private static final class PendingFillRequest
+ extends PendingRequest<RemoteFillService, IAutoFillService> {
private final FillRequest mRequest;
private final IFillCallback mCallback;
private ICancellationSignal mCancellation;
@@ -282,7 +279,7 @@
if (remoteService != null) {
if (sVerbose) Slog.v(mTag, "calling onFillRequest() for id=" + mRequest.getId());
try {
- remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
+ remoteService.mService.onFillRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(mTag, "Error calling on fill request", e);
@@ -310,7 +307,8 @@
}
}
- private static final class PendingSaveRequest extends PendingRequest<RemoteFillService> {
+ private static final class PendingSaveRequest
+ extends PendingRequest<RemoteFillService, IAutoFillService> {
private final SaveRequest mRequest;
private final ISaveCallback mCallback;
@@ -355,7 +353,7 @@
if (remoteService != null) {
if (sVerbose) Slog.v(mTag, "calling onSaveRequest()");
try {
- remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
+ remoteService.mService.onSaveRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(mTag, "Error calling on save request", e);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4c64507..0e9b407 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -95,12 +95,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
-import com.android.server.AbstractRemoteService;
import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
-import com.android.server.intelligence.IntelligenceManagerInternal;
-import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -248,9 +245,18 @@
@GuardedBy("mLock")
private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1);
+ /**
+ * Destroys the augmented Autofill UI.
+ */
+ // TODO(b/111330312): this runnable is called when the Autofill session is destroyed, the
+ // main reason being the cases where user tap HOME.
+ // Right now it's completely destroying the UI, but we need to decide whether / how to
+ // properly recover it later (for example, if the user switches back to the activity,
+ // should it be restored? Right not it kind of is, because Autofill's Session trigger a
+ // new FillRequest, which in turn triggers the Augmented Autofill request again)
@GuardedBy("mLock")
@Nullable
- private AugmentedAutofillCallback mAugmentedAutofillCallback;
+ private Runnable mAugmentedAutofillDestroyer;
/**
* Receiver of assist data from the app's {@link Activity}.
@@ -902,7 +908,7 @@
// VultureCallback
@Override
- public void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
+ public void onServiceDied(@NonNull RemoteFillService service) {
Slog.w(TAG, "removing session because service died");
forceRemoveSelfLocked();
}
@@ -1231,7 +1237,15 @@
return;
}
- final UserData userData = mService.getUserData();
+ final UserData packageUserData = lastResponse.getUserData();
+
+ final UserData userData;
+ if (packageUserData != null) {
+ // Replace default userData
+ userData = packageUserData;
+ } else {
+ userData = mService.getUserData();
+ }
for (int i = 0; i < mViewStates.size(); i++) {
final ViewState viewState = mViewStates.valueAt(i);
@@ -1387,6 +1401,12 @@
final String[] userValues = userData.getValues();
final String[] categoryIds = userData.getCategoryIds();
+ final String defaultAlgorithm = userData.getFieldClassificationAlgorithm();
+ final Bundle defaultArgs = userData.getDefaultFieldClassificationArgs();
+
+ final ArrayMap<String, String> algorithms = userData.getFieldClassificationAlgorithms();
+ final ArrayMap<String, Bundle> args = userData.getFieldClassificationArgs();
+
// Sanity check
if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) {
final int valuesLength = userValues == null ? -1 : userValues.length;
@@ -1402,8 +1422,6 @@
final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>(
maxFieldsSize);
- final String algorithm = userData.getFieldClassificationAlgorithm();
- final Bundle algorithmArgs = userData.getAlgorithmArgs();
final int viewsSize = viewStates.size();
// First, we get all scores.
@@ -1490,7 +1508,8 @@
mComponentName, mCompatMode);
});
- fcStrategy.getScores(callback, algorithm, algorithmArgs, currentValues, userValues);
+ fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
+ defaultAlgorithm, defaultArgs, algorithms, args);
}
/**
@@ -2516,8 +2535,8 @@
// The default autofill service cannot fullfill the request, let's check if the intelligence
// service can.
- mAugmentedAutofillCallback = triggerAugmentedAutofillLocked();
- if (mAugmentedAutofillCallback == null) {
+ mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked();
+ if (mAugmentedAutofillDestroyer == null) {
if (sVerbose) {
Slog.v(TAG, "canceling session " + id + " when server returned null and there is no"
+ " AugmentedAutofill for user");
@@ -2539,21 +2558,22 @@
/**
* Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
*
- * @return callback to the Augmented Autofill service, or {@code null} if not supported.
+ * @return callback to destroy the autofill UI, or {@code null} if not supported.
*/
// TODO(b/111330312): might need to call it in other places, like when the service returns a
// non-null response but without datasets (for example, just SaveInfo)
@GuardedBy("mLock")
- private AugmentedAutofillCallback triggerAugmentedAutofillLocked() {
+ private Runnable triggerAugmentedAutofillLocked() {
// Check if Smart Suggestions is supported...
final @SmartSuggestionMode int supportedModes = mService
.getSupportedSmartSuggestionModesLocked();
if (supportedModes == 0) return null;
// ...then if the service is set for the user
- final IntelligenceManagerInternal intelligenceManagerInternal = mService
- .getMaster().mIntelligenceManagerInternal;
- if (intelligenceManagerInternal == null) return null;
+
+ final RemoteAugmentedAutofillService remoteService = mService
+ .getRemoteAugmentedAutofillServiceLocked();
+ if (remoteService == null) return null;
// Define which mode will be used
final int mode;
@@ -2579,11 +2599,18 @@
+ " when server returned null for session " + this.id);
}
+ final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue();
+
// TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
// furgher AFM -> AFMS calls.
// TODO(b/119638958): add CTS tests
- return intelligenceManagerInternal.requestAutofill(mService.getUserId(), mClient,
- mActivityToken, this.id, mCurrentViewId);
+ remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, mCurrentViewId,
+ currentValue);
+
+ if (mAugmentedAutofillDestroyer == null) {
+ mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(id);
+ }
+ return mAugmentedAutofillDestroyer;
}
@GuardedBy("mLock")
@@ -2864,8 +2891,8 @@
pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
- if (mAugmentedAutofillCallback != null) {
- pw.print(prefix); pw.println("has AugmentedAutofillCallback");
+ if (mAugmentedAutofillDestroyer != null) {
+ pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
}
mRemoteFillService.dump(prefix, pw);
}
@@ -3043,8 +3070,9 @@
@GuardedBy("mLock")
void destroyAugmentedAutofillWindowsLocked() {
- if (mAugmentedAutofillCallback != null) {
- mAugmentedAutofillCallback.destroy();
+ if (mAugmentedAutofillDestroyer != null) {
+ mAugmentedAutofillDestroyer.run();
+ mAugmentedAutofillDestroyer = null;
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 0b06f28..a917ced 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,7 +16,9 @@
package com.android.server.backup;
+import android.Manifest;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -27,10 +29,10 @@
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Environment;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -38,8 +40,6 @@
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -82,11 +82,12 @@
return sInstance;
}
- /** Helper to create the {@link BackupManagerService} instance. */
- public static BackupManagerService create(
- Context context,
- Trampoline parent,
- HandlerThread backupThread) {
+ private final Context mContext;
+ private UserBackupManagerService mUserBackupManagerService;
+
+ /** Instantiate a new instance of {@link BackupManagerService}. */
+ public BackupManagerService(
+ Context context, Trampoline trampoline, HandlerThread backupThread) {
// Set up our transport options and initialize the default transport
SystemConfig systemConfig = SystemConfig.getInstance();
Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
@@ -94,50 +95,24 @@
transportWhitelist = Collections.emptySet();
}
- String transport =
- Settings.Secure.getString(
- context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
- if (TextUtils.isEmpty(transport)) {
- transport = null;
- }
- if (DEBUG) {
- Slog.v(TAG, "Starting with transport " + transport);
- }
- TransportManager transportManager =
- new TransportManager(
- context,
- transportWhitelist,
- transport);
-
- // If encrypted file systems is enabled or disabled, this call will return the
- // correct directory.
- File baseStateDir = new File(Environment.getDataDirectory(), "backup");
-
- // This dir on /cache is managed directly in init.rc
- File dataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
-
- return new BackupManagerService(
- context,
- parent,
- backupThread,
- baseStateDir,
- dataDir,
- transportManager);
+ mContext = context;
+ mUserBackupManagerService =
+ UserBackupManagerService.createAndInitializeService(
+ context, trampoline, backupThread, transportWhitelist);
}
- private UserBackupManagerService mUserBackupManagerService;
-
- /** Instantiate a new instance of {@link BackupManagerService}. */
- public BackupManagerService(
- Context context,
- Trampoline trampoline,
- HandlerThread backupThread,
- File baseStateDir,
- File dataDir,
- TransportManager transportManager) {
- mUserBackupManagerService =
- new UserBackupManagerService(
- context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+ /**
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id on which the backup operation is being requested.
+ * @param message A message to include in the exception if it is thrown.
+ */
+ private void enforceCallingPermissionOnUserId(int userId, String message) {
+ if (Binder.getCallingUserHandle().getIdentifier() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ }
}
// TODO(b/118520567): Remove when tests are modified to use per-user instance.
@@ -151,30 +126,6 @@
* a background thread to keep the unlock time down.
*/
public void unlockSystemUser() {
- // Migrate legacy setting
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
- if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
- if (DEBUG) {
- Slog.i(TAG, "Backup enable apparently not migrated");
- }
- ContentResolver resolver = sInstance.getContext().getContentResolver();
- int enableState = Settings.Secure.getIntForUser(resolver,
- Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
- if (enableState >= 0) {
- if (DEBUG) {
- Slog.i(TAG, "Migrating enable state " + (enableState != 0));
- }
- writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
- Settings.Secure.putStringForUser(resolver,
- Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Backup not yet configured; retaining null enable state");
- }
- }
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
try {
sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
@@ -184,6 +135,15 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+ /**
+ * Starts the backup service for user {@code userId} by creating a new instance of {@link
+ * UserBackupManagerService} and registering it with this service.
+ */
+ // TODO(b/120212806): Add UserBackupManagerService initialization logic.
+ void startServiceForUser(int userId) {
+ // Intentionally empty.
+ }
+
/*
* The following methods are implementations of IBackupManager methods called from Trampoline.
* They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
@@ -373,7 +333,8 @@
// ---------------------------------------------
/** Enable/disable the backup service. This is user-configurable via backup settings. */
- public void setBackupEnabled(boolean enable) {
+ public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
+ enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
mUserBackupManagerService.setBackupEnabled(enable);
}
@@ -390,7 +351,8 @@
/**
* Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
- public boolean isBackupEnabled() {
+ public boolean isBackupEnabled(@UserIdInt int userId) {
+ enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
return mUserBackupManagerService.isBackupEnabled();
}
@@ -414,7 +376,8 @@
* Run a backup pass immediately for any key-value backup applications that have declared that
* they have pending updates.
*/
- public void backupNow() {
+ public void backupNow(@UserIdInt int userId) {
+ enforceCallingPermissionOnUserId(userId, "backupNow");
mUserBackupManagerService.backupNow();
}
@@ -423,12 +386,18 @@
* IBackupManagerMonitor} for receiving events during the operation.
*/
public int requestBackup(
- String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) {
+ @UserIdInt int userId,
+ String[] packages,
+ IBackupObserver observer,
+ IBackupManagerMonitor monitor,
+ int flags) {
+ enforceCallingPermissionOnUserId(userId, "requestBackup");
return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
}
/** Cancel all running backup operations. */
- public void cancelBackups() {
+ public void cancelBackups(@UserIdInt int userId) {
+ enforceCallingPermissionOnUserId(userId, "cancelBackups");
mUserBackupManagerService.cancelBackups();
}
@@ -563,12 +532,6 @@
mUserBackupManagerService.dump(fd, pw, args);
}
- private static boolean backupSettingMigrated(int userId) {
- File base = new File(Environment.getDataDirectory(), "backup");
- File enableFile = new File(base, BACKUP_ENABLE_FILE);
- return enableFile.exists();
- }
-
private static boolean readBackupEnableState(int userId) {
File base = new File(Environment.getDataDirectory(), "backup");
File enableFile = new File(base, BACKUP_ENABLE_FILE);
@@ -598,14 +561,10 @@
stage.renameTo(enableFile);
// will be synced immediately by the try-with-resources call to close()
} catch (IOException | RuntimeException e) {
- // Whoops; looks like we're doomed. Roll everything out, disabled,
- // including the legacy state.
- Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
- + e.getMessage());
-
- ContentResolver resolver = sInstance.getContext().getContentResolver();
- Settings.Secure.putStringForUser(resolver,
- Settings.Secure.BACKUP_ENABLED, null, userId);
+ Slog.e(
+ TAG,
+ "Unable to record backup enable state; reverting to disabled: "
+ + e.getMessage());
enableFile.delete();
stage.delete();
}
@@ -626,7 +585,9 @@
@Override
public void onUnlockUser(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
- sInstance.unlockSystemUser();
+ sInstance.initializeServiceAndUnlockSystemUser();
+ } else {
+ sInstance.startServiceForUser(userId);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 59629aa..ed6ff9b 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -19,6 +19,7 @@
import static com.android.server.backup.BackupManagerService.TAG;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManager;
@@ -41,6 +42,7 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -81,6 +83,12 @@
// Product-level suppression of backup/restore.
private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
+ private static final String BACKUP_THREAD = "backup";
+
+ /** Values for setting {@link Settings.Global#BACKUP_MULTI_USER_ENABLED} */
+ private static final int MULTI_USER_DISABLED = 0;
+ private static final int MULTI_USER_ENABLED = 1;
+
private final Context mContext;
@GuardedBy("mStateLock")
@@ -91,18 +99,35 @@
private volatile BackupManagerService mService;
private HandlerThread mHandlerThread;
+ private Handler mHandler;
public Trampoline(Context context) {
mContext = context;
mGlobalDisable = isBackupDisabled();
mSuppressFile = getSuppressFile();
mSuppressFile.getParentFile().mkdirs();
+
+ mHandlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
}
protected boolean isBackupDisabled() {
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
+ protected boolean isMultiUserEnabled() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.BACKUP_MULTI_USER_ENABLED,
+ MULTI_USER_DISABLED)
+ == MULTI_USER_ENABLED;
+ }
+
+ protected int binderGetCallingUserId() {
+ return Binder.getCallingUserHandle().getIdentifier();
+ }
+
protected int binderGetCallingUid() {
return Binder.getCallingUid();
}
@@ -117,7 +142,7 @@
}
protected BackupManagerService createBackupManagerService() {
- return BackupManagerService.create(mContext, this, mHandlerThread);
+ return new BackupManagerService(mContext, this, mHandlerThread);
}
/**
@@ -147,15 +172,9 @@
/**
* Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
* to initialize {@link BackupManagerService} and set backup state for the system user.
- *
- * @see BackupManagerService#unlockSystemUser()
*/
- void unlockSystemUser() {
- mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
- mHandlerThread.start();
-
- Handler h = new Handler(mHandlerThread.getLooper());
- h.post(
+ void initializeServiceAndUnlockSystemUser() {
+ mHandler.post(
() -> {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
initializeService(UserHandle.USER_SYSTEM);
@@ -170,6 +189,29 @@
}
/**
+ * Called from {@link BackupManagerService.Lifecycle} when a non-system user {@code userId} is
+ * unlocked. Starts the backup service for this user if the service supports multi-user.
+ * Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time low.
+ */
+ // TODO(b/120212806): Consolidate service start for system and non-system users when system
+ // user-only logic is removed.
+ void startServiceForUser(int userId) {
+ if (!isMultiUserEnabled()) {
+ Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
+ return;
+ }
+
+ mHandler.post(
+ () -> {
+ BackupManagerService service = mService;
+ if (service != null) {
+ Slog.i(TAG, "Starting service for user: " + userId);
+ service.startServiceForUser(userId);
+ }
+ });
+ }
+
+ /**
* Only privileged callers should be changing the backup state. This method only acts on {@link
* UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
* system user also deactivates backup in all users.
@@ -282,14 +324,20 @@
}
@Override
- public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+ public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.setBackupEnabled(isEnabled);
+ svc.setBackupEnabled(userId, isEnabled);
}
}
@Override
+ public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+ setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
+ }
+
+ @Override
public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
@@ -306,9 +354,14 @@
}
@Override
- public boolean isBackupEnabled() throws RemoteException {
+ public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.isBackupEnabled() : false;
+ return (svc != null) ? svc.isBackupEnabled(userId) : false;
+ }
+
+ @Override
+ public boolean isBackupEnabled() throws RemoteException {
+ return isBackupEnabledForUser(binderGetCallingUserId());
}
@Override
@@ -324,14 +377,19 @@
}
@Override
- public void backupNow() throws RemoteException {
+ public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.backupNow();
+ svc.backupNow(userId);
}
}
@Override
+ public void backupNow() throws RemoteException {
+ backupNowForUser(binderGetCallingUserId());
+ }
+
+ @Override
public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
boolean includeShared, boolean doWidgets, boolean allApps,
boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames)
@@ -506,21 +564,33 @@
}
@Override
- public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) throws RemoteException {
+ public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
+ observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
BackupManagerService svc = mService;
if (svc == null) {
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
- return svc.requestBackup(packages, observer, monitor, flags);
+ return svc.requestBackup(userId, packages, observer, monitor, flags);
+ }
+
+ @Override
+ public int requestBackup(String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags) throws RemoteException {
+ return requestBackupForUser(binderGetCallingUserId(), packages,
+ observer, monitor, flags);
+ }
+
+ @Override
+ public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.cancelBackups(userId);
+ }
}
@Override
public void cancelBackups() throws RemoteException {
- BackupManagerService svc = mService;
- if (svc != null) {
- svc.cancelBackups();
- }
+ cancelBackupsForUser(binderGetCallingUserId());
}
@Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index fe16afe..5220a59 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -18,6 +18,7 @@
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
@@ -68,6 +69,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -86,6 +88,7 @@
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
@@ -167,6 +170,10 @@
// Persistently track the need to do a full init.
private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+ // Name of the directories the service stores bookkeeping data under.
+ private static final String BACKUP_PERSISTENT_DIR = "backup";
+ private static final String BACKUP_STAGING_DIR = "backup_stage";
+
// System-private key used for backing up an app's widget state. Must
// begin with U+FFxx by convention (we reserve all keys starting
// with U+FF00 or higher for system use).
@@ -360,15 +367,71 @@
private long mAncestralToken = 0;
private long mCurrentToken = 0;
+ /**
+ * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
+ * includes setting up the directories where we keep our bookkeeping and transport management.
+ *
+ * @see #createAndInitializeService(Context, Trampoline, HandlerThread, File, File,
+ * TransportManager)
+ */
+ static UserBackupManagerService createAndInitializeService(
+ Context context,
+ Trampoline trampoline,
+ HandlerThread backupThread,
+ Set<ComponentName> transportWhitelist) {
+ String currentTransport =
+ Settings.Secure.getString(
+ context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+ if (TextUtils.isEmpty(currentTransport)) {
+ currentTransport = null;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Starting with transport " + currentTransport);
+ }
+ TransportManager transportManager =
+ new TransportManager(context, transportWhitelist, currentTransport);
+
+ File baseStateDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+
+ // This dir on /cache is managed directly in init.rc
+ File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
+
+ return createAndInitializeService(
+ context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+ }
+
+ /**
+ * Creates an instance of {@link UserBackupManagerService}.
+ *
+ * @param context The system server context.
+ * @param trampoline A reference to the proxy to {@link BackupManagerService}.
+ * @param backupThread The thread running backup/restore operations for the user.
+ * @param baseStateDir The directory we store the user's persistent bookkeeping data.
+ * @param dataDir The directory we store the user's temporary staging data.
+ * @param transportManager The {@link TransportManager} responsible for handling the user's
+ * transports.
+ */
@VisibleForTesting
- public UserBackupManagerService(
+ public static UserBackupManagerService createAndInitializeService(
+ Context context,
+ Trampoline trampoline,
+ HandlerThread backupThread,
+ File baseStateDir,
+ File dataDir,
+ TransportManager transportManager) {
+ return new UserBackupManagerService(
+ context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+ }
+
+ private UserBackupManagerService(
Context context,
Trampoline parent,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
- mContext = context;
+ mContext = checkNotNull(context, "context cannot be null");
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManager.getService();
@@ -377,6 +440,7 @@
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
+ checkNotNull(parent, "trampoline cannot be null");
mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
mAgentTimeoutParameters = new
@@ -384,6 +448,7 @@
mAgentTimeoutParameters.start();
// spin up the backup/restore handler thread
+ checkNotNull(backupThread, "backupThread cannot be null");
mBackupHandler = new BackupHandler(this, backupThread.getLooper());
// Set up our bookkeeping
@@ -398,13 +463,13 @@
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
false, mProvisionedObserver);
- mBaseStateDir = baseStateDir;
+ mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
mBaseStateDir.mkdirs();
if (!SELinux.restorecon(mBaseStateDir)) {
Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
}
- mDataDir = dataDir;
+ mDataDir = checkNotNull(dataDir, "dataDir cannot be null");
mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
@@ -451,7 +516,7 @@
addPackageParticipantsLocked(null);
}
- mTransportManager = transportManager;
+ mTransportManager = checkNotNull(transportManager, "transportManager cannot be null");
mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
mBackupHandler.postDelayed(
@@ -465,7 +530,6 @@
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
}
-
public BackupManagerConstants getConstants() {
return mConstants;
}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
new file mode 100644
index 0000000..f356b4f
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.Slog;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Wraps a {@link RecoveryController}'s {@link SecretKey}. These are kept in "AndroidKeyStore" (a
+ * provider for {@link java.security.KeyStore} and {@link javax.crypto.KeyGenerator}. They are also
+ * synced with the recoverable key store, wrapped by the primary key. This allows them to be
+ * recovered on a user's subsequent device through providing their lock screen secret.
+ */
+public class RecoverableKeyStoreSecondaryKey {
+ private static final String TAG = "RecoverableKeyStoreSecondaryKey";
+
+ private final String mAlias;
+ private final SecretKey mSecretKey;
+
+ /**
+ * A new instance.
+ *
+ * @param alias The alias. It is keyed with this in AndroidKeyStore and the recoverable key
+ * store.
+ * @param secretKey The key.
+ */
+ public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) {
+ mAlias = checkNotNull(alias);
+ mSecretKey = checkNotNull(secretKey);
+ }
+
+ /**
+ * The ID, as stored in the recoverable {@link java.security.KeyStore}, and as used to identify
+ * wrapped tertiary keys on the backup server.
+ */
+ public String getAlias() {
+ return mAlias;
+ }
+
+ /** The secret key, to be used to wrap tertiary keys. */
+ public SecretKey getSecretKey() {
+ return mSecretKey;
+ }
+
+ /**
+ * The status of the key. i.e., whether it's been synced to remote trusted hardware.
+ *
+ * @param context The application context.
+ * @return One of {@link Status#SYNCED}, {@link Status#NOT_SYNCED} or {@link Status#DESTROYED}.
+ */
+ public @Status int getStatus(Context context) {
+ try {
+ return getStatusInternal(context);
+ } catch (InternalRecoveryServiceException e) {
+ Slog.wtf(TAG, "Internal error getting recovery status", e);
+ // Return NOT_SYNCED by default, as we do not want the backups to fail or to repeatedly
+ // attempt to reinitialize.
+ return Status.NOT_SYNCED;
+ }
+ }
+
+ private @Status int getStatusInternal(Context context) throws InternalRecoveryServiceException {
+ int status = RecoveryController.getInstance(context).getRecoveryStatus(mAlias);
+ switch (status) {
+ case RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE:
+ return Status.DESTROYED;
+ case RecoveryController.RECOVERY_STATUS_SYNCED:
+ return Status.SYNCED;
+ case RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS:
+ return Status.NOT_SYNCED;
+ default:
+ // Throw an exception if we encounter a status that doesn't match any of the above.
+ throw new InternalRecoveryServiceException(
+ "Unexpected status from getRecoveryStatus: " + status);
+ }
+ }
+
+ /** Status of a key in the recoverable key store. */
+ @IntDef({Status.NOT_SYNCED, Status.SYNCED, Status.DESTROYED})
+ public @interface Status {
+ /**
+ * The key has not yet been synced to remote trusted hardware. This may be because the user
+ * has not yet unlocked their device.
+ */
+ int NOT_SYNCED = 1;
+
+ /**
+ * The key has been synced with remote trusted hardware. It should now be recoverable on
+ * another device.
+ */
+ int SYNCED = 2;
+
+ /** The key has been lost forever. This can occur if the user disables their lock screen. */
+ int DESTROYED = 3;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
new file mode 100644
index 0000000..db5fe77
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.ByteStringUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.util.Optional;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Manages generating, deleting, and retrieving secondary keys through {@link RecoveryController}.
+ *
+ * <p>The recoverable key store will be synced remotely via the {@link RecoveryController}, allowing
+ * recovery of keys on other devices owned by the user.
+ */
+public class RecoverableKeyStoreSecondaryKeyManager {
+ private static final String BACKUP_KEY_ALIAS_PREFIX =
+ "com.android.server.backup/recoverablekeystore/";
+ private static final int BACKUP_KEY_SUFFIX_LENGTH_BITS = 128;
+ private static final int BITS_PER_BYTE = 8;
+
+ /** A new instance. */
+ public static RecoverableKeyStoreSecondaryKeyManager getInstance(Context context) {
+ return new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(context), new SecureRandom());
+ }
+
+ private final RecoveryController mRecoveryController;
+ private final SecureRandom mSecureRandom;
+
+ @VisibleForTesting
+ public RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController recoveryController, SecureRandom secureRandom) {
+ mRecoveryController = recoveryController;
+ mSecureRandom = secureRandom;
+ }
+
+ /**
+ * Generates a new recoverable key using the {@link RecoveryController}.
+ *
+ * @throws InternalRecoveryServiceException if an unexpected error occurred generating the key.
+ * @throws LockScreenRequiredException if the user does not have a lock screen. A lock screen is
+ * required to generate a recoverable key.
+ */
+ public RecoverableKeyStoreSecondaryKey generate()
+ throws InternalRecoveryServiceException, LockScreenRequiredException,
+ UnrecoverableKeyException {
+ String alias = generateId();
+ mRecoveryController.generateKey(alias);
+ SecretKey key = (SecretKey) mRecoveryController.getKey(alias);
+ if (key == null) {
+ throw new InternalRecoveryServiceException(
+ String.format(
+ "Generated key %s but could not get it back immediately afterwards.",
+ alias));
+ }
+ return new RecoverableKeyStoreSecondaryKey(alias, key);
+ }
+
+ /**
+ * Removes the secondary key. This means the key will no longer be recoverable.
+ *
+ * @param alias The alias of the key.
+ * @throws InternalRecoveryServiceException if there was a {@link RecoveryController} error.
+ */
+ public void remove(String alias) throws InternalRecoveryServiceException {
+ mRecoveryController.removeKey(alias);
+ }
+
+ /**
+ * Returns the {@link RecoverableKeyStoreSecondaryKey} with {@code alias} if it is in the {@link
+ * RecoveryController}. Otherwise, {@link Optional#empty()}.
+ */
+ public Optional<RecoverableKeyStoreSecondaryKey> get(String alias)
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ SecretKey secretKey = (SecretKey) mRecoveryController.getKey(alias);
+ return Optional.ofNullable(secretKey)
+ .map(key -> new RecoverableKeyStoreSecondaryKey(alias, key));
+ }
+
+ /**
+ * Generates a new key alias. This has more entropy than a UUID - it can be considered
+ * universally unique.
+ */
+ private String generateId() {
+ byte[] id = new byte[BACKUP_KEY_SUFFIX_LENGTH_BITS / BITS_PER_BYTE];
+ mSecureRandom.nextBytes(id);
+ return BACKUP_KEY_ALIAS_PREFIX + ByteStringUtils.toHexString(id);
+ }
+
+ /** Constructs a {@link RecoverableKeyStoreSecondaryKeyManager}. */
+ public interface RecoverableKeyStoreSecondaryKeyManagerProvider {
+ /** Returns a newly constructed {@link RecoverableKeyStoreSecondaryKeyManager}. */
+ RecoverableKeyStoreSecondaryKeyManager get();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
new file mode 100644
index 0000000..ebf09df
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/** 256-bit AES key generator. Each app should have its own separate AES key. */
+public class TertiaryKeyGenerator {
+ private static final int KEY_SIZE_BITS = 256;
+ private static final String KEY_ALGORITHM = "AES";
+
+ private final KeyGenerator mKeyGenerator;
+
+ /** New instance generating keys using {@code secureRandom}. */
+ public TertiaryKeyGenerator(SecureRandom secureRandom) {
+ try {
+ mKeyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+ mKeyGenerator.init(KEY_SIZE_BITS, secureRandom);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(
+ "Impossible condition: JCE thinks it does not support AES.", e);
+ }
+ }
+
+ /** Generates a new random AES key. */
+ public SecretKey generate() {
+ return mKeyGenerator.generateKey();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
new file mode 100644
index 0000000..ec90f6c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Locale;
+
+/**
+ * Tracks when a tertiary key rotation is due.
+ *
+ * <p>After a certain number of incremental backups, the device schedules a full backup, which will
+ * generate a new encryption key, effecting a key rotation. We should do this on a regular basis so
+ * that if a key does become compromised it has limited value to the attacker.
+ *
+ * <p>No additional synchronization of this class is provided. Only one instance should be used at
+ * any time. This should be fine as there should be no parallelism in backups.
+ */
+public class TertiaryKeyRotationTracker {
+ private static final int MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION = 31;
+ private static final String SHARED_PREFERENCES_NAME = "tertiary_key_rotation_tracker";
+
+ private static final String TAG = "TertiaryKeyRotationTracker";
+ private static final boolean DEBUG = false;
+
+ /**
+ * A new instance, using {@code context} to commit data to disk via {@link SharedPreferences}.
+ */
+ public static TertiaryKeyRotationTracker getInstance(Context context) {
+ return new TertiaryKeyRotationTracker(
+ context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE));
+ }
+
+ private final SharedPreferences mSharedPreferences;
+
+ /** New instance, storing data in {@code mSharedPreferences}. */
+ @VisibleForTesting
+ TertiaryKeyRotationTracker(SharedPreferences sharedPreferences) {
+ mSharedPreferences = sharedPreferences;
+ }
+
+ /**
+ * Returns {@code true} if the given app is due having its key rotated.
+ *
+ * @param packageName The package name of the app.
+ */
+ public boolean isKeyRotationDue(String packageName) {
+ return getBackupsSinceRotation(packageName) >= MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION;
+ }
+
+ /**
+ * Records that an incremental backup has occurred. Each incremental backup brings the app
+ * closer to the time when its key should be rotated.
+ *
+ * @param packageName The package name of the app for which the backup occurred.
+ */
+ public void recordBackup(String packageName) {
+ int backupsSinceRotation = getBackupsSinceRotation(packageName) + 1;
+ mSharedPreferences.edit().putInt(packageName, backupsSinceRotation).apply();
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ String.format(
+ Locale.US,
+ "Incremental backup for %s. %d backups until key rotation.",
+ packageName,
+ Math.max(
+ 0,
+ MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION
+ - backupsSinceRotation)));
+ }
+ }
+
+ /**
+ * Resets the rotation delay for the given app. Should be invoked after a key rotation.
+ *
+ * @param packageName Package name of the app whose key has rotated.
+ */
+ public void resetCountdown(String packageName) {
+ mSharedPreferences.edit().putInt(packageName, 0).apply();
+ }
+
+ /** Marks all enrolled packages for key rotation. */
+ public void markAllForRotation() {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ for (String packageName : mSharedPreferences.getAll().keySet()) {
+ editor.putInt(packageName, MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION);
+ }
+ editor.apply();
+ }
+
+ private int getBackupsSinceRotation(String packageName) {
+ return mSharedPreferences.getInt(packageName, 0);
+ }
+}
diff --git a/services/intelligence/Android.bp b/services/contentcapture/Android.bp
similarity index 69%
rename from services/intelligence/Android.bp
rename to services/contentcapture/Android.bp
index 2df1235..57e859e 100644
--- a/services/intelligence/Android.bp
+++ b/services/contentcapture/Android.bp
@@ -1,5 +1,5 @@
java_library_static {
- name: "services.intelligence",
+ name: "services.contentcapture",
srcs: ["java/**/*.java"],
libs: ["services.core"],
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
new file mode 100644
index 0000000..872fe42
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.contentcapture;
+
+import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE;
+import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.IContentCaptureManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service used to observe the contents of the screen.
+ *
+ * <p>The data collected by this service can be analyzed and combined with other sources to provide
+ * contextual data in other areas of the system such as Autofill.
+ */
+public final class ContentCaptureManagerService extends
+ AbstractMasterSystemService<ContentCaptureManagerService, ContentCapturePerUserService> {
+
+ private static final String TAG = ContentCaptureManagerService.class.getSimpleName();
+
+ static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
+
+ private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+ @GuardedBy("mLock")
+ private ActivityManagerInternal mAm;
+
+ private final LocalService mLocalService = new LocalService();
+
+ public ContentCaptureManagerService(Context context) {
+ super(context, UserManager.DISALLOW_CONTENT_CAPTURE);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected ContentCapturePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
+ boolean disabled) {
+ return new ContentCapturePerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override // from SystemService
+ public void onStart() {
+ publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE,
+ new ContentCaptureManagerServiceStub());
+ publishLocalService(ContentCaptureManagerInternal.class, mLocalService);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServiceRemoved(@NonNull ContentCapturePerUserService service,
+ @UserIdInt int userId) {
+ service.destroyLocked();
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_CONTENT_CAPTURE, TAG);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_DURATION_MS;
+ }
+
+ // Called by Shell command.
+ void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+ Slog.i(TAG, "destroySessions() for userId " + userId);
+ enforceCallingPermissionForManagement();
+
+ synchronized (mLock) {
+ if (userId != UserHandle.USER_ALL) {
+ final ContentCapturePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.destroySessionsLocked();
+ }
+ } else {
+ visitServicesLocked((s) -> s.destroySessionsLocked());
+ }
+ }
+
+ try {
+ receiver.send(0, new Bundle());
+ } catch (RemoteException e) {
+ // Just ignore it...
+ }
+ }
+
+ // Called by Shell command.
+ void listSessions(int userId, IResultReceiver receiver) {
+ Slog.i(TAG, "listSessions() for userId " + userId);
+ enforceCallingPermissionForManagement();
+
+ final Bundle resultData = new Bundle();
+ final ArrayList<String> sessions = new ArrayList<>();
+
+ synchronized (mLock) {
+ if (userId != UserHandle.USER_ALL) {
+ final ContentCapturePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.listSessionsLocked(sessions);
+ }
+ } else {
+ visitServicesLocked((s) -> s.listSessionsLocked(sessions));
+ }
+ }
+
+ resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
+ try {
+ receiver.send(0, resultData);
+ } catch (RemoteException e) {
+ // Just ignore it...
+ }
+ }
+
+ private ActivityManagerInternal getAmInternal() {
+ synchronized (mLock) {
+ if (mAm == null) {
+ mAm = LocalServices.getService(ActivityManagerInternal.class);
+ }
+ }
+ return mAm;
+ }
+
+ final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
+
+ @Override
+ public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
+ @NonNull ComponentName componentName, @NonNull String sessionId,
+ int flags, @NonNull IResultReceiver result) {
+ Preconditions.checkNotNull(activityToken);
+ Preconditions.checkNotNull(componentName);
+ Preconditions.checkNotNull(sessionId);
+
+ // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
+ // so we don't pass it on startSession (same for Autofill)
+ final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false);
+
+ // TODO(b/111276913): get from AM as well
+ final int displayId = 0;
+
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+ service.startSessionLocked(activityToken, componentName, taskId, displayId,
+ sessionId, flags, mAllowInstantService, result);
+ }
+ }
+
+ @Override
+ public void sendEvents(@UserIdInt int userId, @NonNull String sessionId,
+ @NonNull List<ContentCaptureEvent> events) {
+ Preconditions.checkNotNull(sessionId);
+ Preconditions.checkNotNull(events);
+
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+ service.sendEventsLocked(sessionId, events);
+ }
+ }
+
+ @Override
+ public void finishSession(@UserIdInt int userId, @NonNull String sessionId,
+ @Nullable List<ContentCaptureEvent> events) {
+ Preconditions.checkNotNull(sessionId);
+
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+ service.finishSessionLocked(sessionId, events);
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+ throws RemoteException {
+ new ContentCaptureManagerServiceShellCommand(ContentCaptureManagerService.this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+ }
+
+ private final class LocalService extends ContentCaptureManagerInternal {
+
+ @Override
+ public boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId) {
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.isContentCaptureServiceForUserLocked(uid);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
+ @NonNull Bundle data) {
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.sendActivityAssistDataLocked(activityToken, data);
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
new file mode 100644
index 0000000..2f78276
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.contentcapture;
+
+import static com.android.server.contentcapture.ContentCaptureManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Shell Command implementation for {@link ContentCaptureManagerService}.
+ */
+public final class ContentCaptureManagerServiceShellCommand extends ShellCommand {
+
+ private final ContentCaptureManagerService mService;
+
+ public ContentCaptureManagerServiceShellCommand(@NonNull ContentCaptureManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "list":
+ return requestList(pw);
+ case "destroy":
+ return requestDestroy(pw);
+ case "get":
+ return requestGet(pw);
+ case "set":
+ return requestSet(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter();) {
+ pw.println("ContentCapture Service (content_capture) commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" get bind-instant-service-allowed");
+ pw.println(" Gets whether binding to services provided by instant apps is allowed");
+ pw.println("");
+ pw.println(" set bind-instant-service-allowed [true | false]");
+ pw.println(" Sets whether binding to services provided by instant apps is allowed");
+ pw.println("");
+ pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
+ pw.println(" list sessions [--user USER_ID]");
+ pw.println(" Lists all pending sessions.");
+ pw.println("");
+ pw.println(" destroy sessions [--user USER_ID]");
+ pw.println(" Destroys all pending sessions.");
+ pw.println("");
+ }
+ }
+
+ private int requestGet(PrintWriter pw) {
+ final String what = getNextArgRequired();
+ switch(what) {
+ case "bind-instant-service-allowed":
+ return getBindInstantService(pw);
+ default:
+ pw.println("Invalid set: " + what);
+ return -1;
+ }
+ }
+
+ private int requestSet(PrintWriter pw) {
+ final String what = getNextArgRequired();
+
+ switch(what) {
+ case "bind-instant-service-allowed":
+ return setBindInstantService(pw);
+ case "temporary-service":
+ return setTemporaryService(pw);
+ default:
+ pw.println("Invalid set: " + what);
+ return -1;
+ }
+ }
+
+ private int getBindInstantService(PrintWriter pw) {
+ if (mService.getAllowInstantService()) {
+ pw.println("true");
+ } else {
+ pw.println("false");
+ }
+ return 0;
+ }
+
+ private int setBindInstantService(PrintWriter pw) {
+ final String mode = getNextArgRequired();
+ switch (mode.toLowerCase()) {
+ case "true":
+ mService.setAllowInstantService(true);
+ return 0;
+ case "false":
+ mService.setAllowInstantService(false);
+ return 0;
+ default:
+ pw.println("Invalid mode: " + mode);
+ return -1;
+ }
+ }
+
+ private int setTemporaryService(PrintWriter pw) {
+ final int userId = getNextIntArgRequired();
+ final String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ return 0;
+ }
+ final int duration = getNextIntArgRequired();
+ mService.setTemporaryService(userId, serviceName, duration);
+ pw.println("ContentCaptureService temporarily set to " + serviceName + " for "
+ + duration + "ms");
+ return 0;
+ }
+
+ private int requestDestroy(PrintWriter pw) {
+ if (!isNextArgSessions(pw)) {
+ return -1;
+ }
+
+ final int userId = getUserIdFromArgsOrAllUsers();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final IResultReceiver receiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ latch.countDown();
+ }
+ };
+ return requestSessionCommon(pw, latch, () -> mService.destroySessions(userId, receiver));
+ }
+
+ private int requestList(PrintWriter pw) {
+ if (!isNextArgSessions(pw)) {
+ return -1;
+ }
+
+ final int userId = getUserIdFromArgsOrAllUsers();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final IResultReceiver receiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ final ArrayList<String> sessions = resultData
+ .getStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS);
+ for (String session : sessions) {
+ pw.println(session);
+ }
+ latch.countDown();
+ }
+ };
+ return requestSessionCommon(pw, latch, () -> mService.listSessions(userId, receiver));
+ }
+
+ private boolean isNextArgSessions(PrintWriter pw) {
+ final String type = getNextArgRequired();
+ if (!type.equals("sessions")) {
+ pw.println("Error: invalid list type");
+ return false;
+ }
+ return true;
+ }
+
+ private int requestSessionCommon(PrintWriter pw, CountDownLatch latch,
+ Runnable command) {
+ command.run();
+ return waitForLatch(pw, latch);
+ }
+
+ private int waitForLatch(PrintWriter pw, CountDownLatch latch) {
+ try {
+ final boolean received = latch.await(5, TimeUnit.SECONDS);
+ if (!received) {
+ pw.println("Timed out after 5 seconds");
+ return -1;
+ }
+ } catch (InterruptedException e) {
+ pw.println("System call interrupted");
+ Thread.currentThread().interrupt();
+ return -1;
+ }
+ return 0;
+ }
+
+ private int getUserIdFromArgsOrAllUsers() {
+ if ("--user".equals(getNextArg())) {
+ return UserHandle.parseUserArg(getNextArgRequired());
+ }
+ return UserHandle.USER_ALL;
+ }
+
+ private int getNextIntArgRequired() {
+ return Integer.parseInt(getNextArgRequired());
+ }
+}
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
similarity index 74%
rename from services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
rename to services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index e3b09c6..aa171f4 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.intelligence;
+package com.android.server.contentcapture;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
@@ -34,65 +34,70 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.service.intelligence.InteractionSessionId;
-import android.service.intelligence.SnapshotData;
+import android.service.contentcapture.SnapshotData;
import android.util.ArrayMap;
import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
-import android.view.intelligence.ContentCaptureEvent;
-import android.view.intelligence.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
-import com.android.server.AbstractPerUserSystemService;
-import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
+import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
- * Per-user instance of {@link IntelligenceManagerService}.
+ * Per-user instance of {@link ContentCaptureManagerService}.
*/
-//TODO(b/111276913): rename once the final name is defined
-final class IntelligencePerUserService
- extends AbstractPerUserSystemService<IntelligencePerUserService,
- IntelligenceManagerService> {
+final class ContentCapturePerUserService
+ extends
+ AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService> {
- private static final String TAG = "IntelligencePerUserService";
+ private static final String TAG = ContentCaptureManagerService.class.getSimpleName();
@GuardedBy("mLock")
- private final ArrayMap<InteractionSessionId, ContentCaptureSession> mSessions =
+ private final ArrayMap<String, ContentCaptureSession> mSessions =
new ArrayMap<>();
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
- protected IntelligencePerUserService(
- IntelligenceManagerService master, Object lock, @UserIdInt int userId) {
- super(master, lock, userId);
+ protected ContentCapturePerUserService(
+ ContentCaptureManagerService master, Object lock, @UserIdInt int userId) {
+ super(master, new FrameworkResourcesServiceNameResolver(master.getContext(), userId, lock,
+ com.android.internal.R.string.config_defaultContentCaptureService), lock, userId);
}
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+ final boolean isTemp = isTemporaryServiceSetLocked();
+ if (!isTemp) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
ServiceInfo si;
try {
- // TODO(b/111276913): must check that either the service is from a system component,
- // or it matches a service set by shell cmd (so it can be used on CTS tests and when
- // OEMs are implementing the real service
- si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
- PackageManager.GET_META_DATA, mUserId);
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, mUserId);
} catch (RemoteException e) {
Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
return null;
}
- if (!Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE.equals(si.permission)) {
- Slog.w(TAG, "SmartSuggestionsService from '" + si.packageName
+ if (si == null) {
+ Slog.w(TAG, "Could not get serviceInfo for " + (isTemp ? " (temp)" : "(default system)")
+ + " " + serviceComponent.flattenToShortString());
+ return null;
+ }
+ if (!Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE.equals(si.permission)) {
+ Slog.w(TAG, "ContentCaptureService from '" + si.packageName
+ "' does not require permission "
- + Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE);
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
throw new SecurityException("Service does not require permission "
- + Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE);
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
}
return si;
}
@@ -108,7 +113,7 @@
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
@NonNull ComponentName componentName, int taskId, int displayId,
- @NonNull InteractionSessionId sessionId, int flags,
+ @NonNull String sessionId, int flags, boolean bindInstantServiceAllowed,
@NonNull IResultReceiver resultReceiver) {
if (!isEnabledLocked()) {
sendToClient(resultReceiver, ContentCaptureManager.STATE_DISABLED);
@@ -138,9 +143,6 @@
return;
}
- // TODO(b/117779333): get from mMaster once it's moved to superclass
- final boolean bindInstantServiceAllowed = false;
-
session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
bindInstantServiceAllowed, mMaster.verbose);
@@ -155,7 +157,7 @@
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
- public void finishSessionLocked(@NonNull InteractionSessionId sessionId,
+ public void finishSessionLocked(@NonNull String sessionId,
@Nullable List<ContentCaptureEvent> events) {
if (!isEnabledLocked()) {
return;
@@ -172,7 +174,7 @@
// TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
// calls because it's not clear yet whether we'll change the manager to send events
// to the service directly (i.e., without passing through system server). Once we
- // decide, we might need to split IIntelligenceService.onSessionLifecycle() in 2
+ // decide, we might need to split IContentCaptureManager.onSessionLifecycle() in 2
// methods, one for start and another for finish (and passing the events to finish),
// otherwise the service might receive the 2 calls out of order.
session.sendEventsLocked(events);
@@ -185,9 +187,9 @@
}
// TODO(b/111276913): need to figure out why some events are sent before session is started;
- // probably because IntelligenceManager is not buffering them until it gets the session back
+ // probably because ContentCaptureManager is not buffering them until it gets the session back
@GuardedBy("mLock")
- public void sendEventsLocked(@NonNull InteractionSessionId sessionId,
+ public void sendEventsLocked(@NonNull String sessionId,
@NonNull List<ContentCaptureEvent> events) {
if (!isEnabledLocked()) {
return;
@@ -208,7 +210,7 @@
@GuardedBy("mLock")
public boolean sendActivityAssistDataLocked(@NonNull IBinder activityToken,
@NonNull Bundle data) {
- final InteractionSessionId id = getInteractionSessionId(activityToken);
+ final String id = getSessionId(activityToken);
if (id != null) {
final ContentCaptureSession session = mSessions.get(id);
final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
@@ -225,12 +227,12 @@
}
@GuardedBy("mLock")
- public void removeSessionLocked(@NonNull InteractionSessionId sessionId) {
+ public void removeSessionLocked(@NonNull String sessionId) {
mSessions.remove(sessionId);
}
@GuardedBy("mLock")
- public boolean isIntelligenceServiceForUserLocked(int uid) {
+ public boolean isContentCaptureServiceForUserLocked(int uid) {
return uid == getServiceUidLocked();
}
@@ -253,6 +255,11 @@
@GuardedBy("mLock")
public void destroyLocked() {
if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
+ destroySessionsLocked();
+ }
+
+ @GuardedBy("mLock")
+ void destroySessionsLocked() {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
final ContentCaptureSession session = mSessions.valueAt(i);
@@ -261,19 +268,12 @@
mSessions.clear();
}
- public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
- @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
- synchronized (mLock) {
- final ContentCaptureSession session = getSession(activityToken);
- if (session != null) {
- // TODO(b/111330312): log metrics
- if (mMaster.verbose) Slog.v(TAG, "requestAugmentedAutofill()");
- return session.requestAutofillLocked(client, autofillSessionId, focusedId);
- }
- if (mMaster.debug) {
- Slog.d(TAG, "requestAutofill(): no session for " + activityToken);
- }
- return null;
+ @GuardedBy("mLock")
+ void listSessionsLocked(ArrayList<String> output) {
+ final int numSessions = mSessions.size();
+ for (int i = 0; i < numSessions; i++) {
+ final ContentCaptureSession session = mSessions.valueAt(i);
+ output.add(session.toShortString());
}
}
@@ -295,10 +295,10 @@
}
/**
- * Returns the InteractionSessionId associated with the given activity.
+ * Returns the session id associated with the given activity.
*/
@GuardedBy("mLock")
- private InteractionSessionId getInteractionSessionId(@NonNull IBinder activityToken) {
+ private String getSessionId(@NonNull IBinder activityToken) {
for (int i = 0; i < mSessions.size(); i++) {
ContentCaptureSession session = mSessions.valueAt(i);
if (session.isActivitySession(activityToken)) {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
new file mode 100644
index 0000000..a4012d5
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.contentcapture;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.contentcapture.ContentCaptureService;
+import android.service.contentcapture.InteractionContext;
+import android.service.contentcapture.InteractionSessionId;
+import android.service.contentcapture.SnapshotData;
+import android.util.Slog;
+import android.view.contentcapture.ContentCaptureEvent;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+final class ContentCaptureSession implements ContentCaptureServiceCallbacks {
+
+ private static final String TAG = "ContentCaptureSession";
+
+ private final Object mLock;
+ final IBinder mActivityToken;
+ private final ContentCapturePerUserService mService;
+ private final RemoteContentCaptureService mRemoteService;
+ private final InteractionContext mInterationContext;
+ private final String mId;
+
+ ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
+ @NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
+ @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
+ int taskId, int displayId, @NonNull String sessionId, int flags,
+ boolean bindInstantServiceAllowed, boolean verbose) {
+ mLock = lock;
+ mActivityToken = activityToken;
+ mService = service;
+ mId = Preconditions.checkNotNull(sessionId);
+ mRemoteService = new RemoteContentCaptureService(context,
+ ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
+ bindInstantServiceAllowed, verbose);
+ mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
+ }
+
+ /**
+ * Returns whether this session is for the given activity.
+ */
+ boolean isActivitySession(@NonNull IBinder activityToken) {
+ return mActivityToken.equals(activityToken);
+ }
+
+ /**
+ * Notifies the {@link ContentCaptureService} that the service started.
+ */
+ @GuardedBy("mLock")
+ public void notifySessionStartedLocked() {
+ mRemoteService.onSessionLifecycleRequest(mInterationContext, mId);
+ }
+
+ /**
+ * Notifies the {@link ContentCaptureService} of a batch of events.
+ */
+ public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
+ mRemoteService.onContentCaptureEventsRequest(mId, events);
+ }
+
+ /**
+ * Notifies the {@link ContentCaptureService} of a snapshot of an activity.
+ */
+ @GuardedBy("mLock")
+ public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
+ mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
+ }
+
+ /**
+ * Cleans up the session and removes it from the service.
+ *
+ * @param notifyRemoteService whether it should trigger a {@link
+ * ContentCaptureService#onDestroyInteractionSession(InteractionSessionId)}
+ * request.
+ */
+ @GuardedBy("mLock")
+ public void removeSelfLocked(boolean notifyRemoteService) {
+ try {
+ destroyLocked(notifyRemoteService);
+ } finally {
+ mService.removeSessionLocked(mId);
+ }
+ }
+
+ /**
+ * Cleans up the session, but not removes it from the service.
+ *
+ * @param notifyRemoteService whether it should trigger a {@link
+ * ContentCaptureService#onDestroyInteractionSession(InteractionSessionId)}
+ * request.
+ */
+ @GuardedBy("mLock")
+ public void destroyLocked(boolean notifyRemoteService) {
+ if (mService.isVerbose()) {
+ Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
+ }
+ // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
+ if (notifyRemoteService) {
+ mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+ }
+ }
+
+ @Override // from RemoteContentCaptureServiceCallbacks
+ public void onServiceDied(@NonNull RemoteContentCaptureService service) {
+ // TODO(b/111276913): implement (remove session from PerUserSession?)
+ if (mService.isDebug()) {
+ Slog.d(TAG, "onServiceDied() for " + mId);
+ }
+ synchronized (mLock) {
+ removeSelfLocked(/* notifyRemoteService= */ false);
+ }
+ }
+
+ @GuardedBy("mLock")
+ public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println();
+ pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println();
+ pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+ pw.print(prefix); pw.print("has autofill callback: ");
+ }
+
+ String toShortString() {
+ return mId + ":" + mActivityToken;
+ }
+
+ @Override
+ public String toString() {
+ return "ContentCaptureSession[id=" + mId + ", act=" + mActivityToken + "]";
+ }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
new file mode 100644
index 0000000..33b6c8d
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.contentcapture.ContentCaptureEventsRequest;
+import android.service.contentcapture.IContentCaptureService;
+import android.service.contentcapture.InteractionContext;
+import android.service.contentcapture.SnapshotData;
+import android.text.format.DateUtils;
+import android.view.contentcapture.ContentCaptureEvent;
+
+import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
+
+import java.util.List;
+
+final class RemoteContentCaptureService
+ extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
+ IContentCaptureService> {
+
+ // TODO(b/117779333): changed it so it's permanentely bound
+ private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+ RemoteContentCaptureService(Context context, String serviceInterface,
+ ComponentName componentName, int userId,
+ ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callbacks,
+ bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
+ }
+
+ @Override // from RemoteService
+ protected IContentCaptureService getServiceInterface(@NonNull IBinder service) {
+ return IContentCaptureService.Stub.asInterface(service);
+ }
+
+ // TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
+ @Override // from RemoteService
+ protected long getTimeoutIdleBindMillis() {
+ // TODO(b/111276913): read from Settings so it can be changed in the field
+ return TIMEOUT_IDLE_BIND_MILLIS;
+ }
+
+ @Override // from RemoteService
+ protected long getRemoteRequestMillis() {
+ // TODO(b/111276913): read from Settings so it can be changed in the field
+ return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ }
+
+ /**
+ * Called by {@link ContentCaptureSession} to generate a call to the
+ * {@link RemoteContentCaptureService} to indicate the session was created (when {@code context}
+ * is not {@code null} or destroyed (when {@code context} is {@code null}).
+ */
+ public void onSessionLifecycleRequest(@Nullable InteractionContext context,
+ @NonNull String sessionId) {
+ scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
+ }
+
+ /**
+ * Called by {@link ContentCaptureSession} to send a batch of events to the service.
+ */
+ public void onContentCaptureEventsRequest(@NonNull String sessionId,
+ @NonNull List<ContentCaptureEvent> events) {
+ scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
+ new ContentCaptureEventsRequest(events)));
+ }
+
+ /**
+ * Called by {@link ContentCaptureSession} to send snapshot data to the service.
+ */
+ public void onActivitySnapshotRequest(@NonNull String sessionId,
+ @NonNull SnapshotData snapshotData) {
+ scheduleAsyncRequest((s) -> s.onActivitySnapshot(sessionId, snapshotData));
+ }
+
+ public interface ContentCaptureServiceCallbacks
+ extends VultureCallback<RemoteContentCaptureService> {
+ // NOTE: so far we don't need to notify the callback implementation (an inner class on
+ // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
+ // callback interface is empty.
+ }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 784d398..4408db8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -7,7 +7,6 @@
"frameworks/native/aidl/binder",
"frameworks/native/cmds/dumpstate/binder",
"system/core/storaged/binder",
- "system/netd/server/binder",
"system/vold/binder",
],
},
@@ -15,8 +14,6 @@
"java/**/*.java",
":dumpstate_aidl",
":idmap2_aidl",
- ":netd_aidl",
- ":netd_metrics_aidl",
":installd_aidl",
":storaged_aidl",
":vold_aidl",
@@ -53,6 +50,7 @@
"android.hardware.contexthub-V1.0-java",
"android.hidl.manager-V1.0-java",
"netd_aidl_interface-java",
+ "netd_event_listener_interface-java",
],
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 854c03f..08034f7 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -48,6 +48,7 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -1306,8 +1307,12 @@
// because kernel doesn't keep this after reboot
setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
- // Also sure that we're booting with a halfway sensible current time
- final long systemBuildTime = Environment.getRootDirectory().lastModified();
+ // Ensure that we're booting with a halfway sensible current time. Use the
+ // most recent of Build.TIME, the root file system's timestamp, and the
+ // value of the ro.build.date.utc system property (which is in seconds).
+ final long systemBuildTime = Long.max(
+ 1000L * SystemProperties.getLong("ro.build.date.utc", -1L),
+ Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
+ ", advancing to build time " + systemBuildTime);
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index d564925..8c5ee7f 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -26,8 +26,6 @@
import android.os.SystemClock;
import android.util.Slog;
-import java.lang.Float;
-
/**
* Determines if the device has been set upon a stationary object.
*/
@@ -140,6 +138,13 @@
}
}
+ /**
+ * If we do not have an accelerometer, we are not going to collect much data.
+ */
+ public boolean hasSensor() {
+ return mAccelSensor != null;
+ }
+
/*
* Acquire accel data until we determine AnyMotion status.
*/
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 9820321..8b7f321 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -16,31 +16,35 @@
package com.android.server;
-import android.app.AppGlobals;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
-import android.os.RemoteException;
+import android.os.Process;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.os.AppIdToPackageMap;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal;
import com.android.internal.os.CachedDeviceState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public class BinderCallsStatsService extends Binder {
@@ -49,6 +53,71 @@
private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
= "persist.sys.binder_calls_detailed_tracking";
+ /** Resolves the work source of an incoming binder transaction. */
+ static class AuthorizedWorkSourceProvider implements BinderInternal.WorkSourceProvider {
+ private ArraySet<Integer> mAppIdWhitelist;
+
+ AuthorizedWorkSourceProvider() {
+ mAppIdWhitelist = new ArraySet<>();
+ }
+
+ public int resolveWorkSourceUid() {
+ final int callingUid = getCallingUid();
+ final int appId = UserHandle.getAppId(callingUid);
+ if (mAppIdWhitelist.contains(appId)) {
+ final int workSource = getCallingWorkSourceUid();
+ final boolean isWorkSourceSet = workSource != Binder.UNSET_WORKSOURCE;
+ return isWorkSourceSet ? workSource : callingUid;
+ }
+ return callingUid;
+ }
+
+ public void systemReady(Context context) {
+ mAppIdWhitelist = createAppidWhitelist(context);
+ }
+
+ public void dump(PrintWriter pw, AppIdToPackageMap packageMap) {
+ pw.println("AppIds of apps that can set the work source:");
+ final ArraySet<Integer> whitelist = mAppIdWhitelist;
+ for (Integer appId : whitelist) {
+ pw.println("\t- " + packageMap.mapAppId(appId));
+ }
+ }
+
+ protected int getCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ protected int getCallingWorkSourceUid() {
+ return Binder.getCallingWorkSourceUid();
+ }
+
+ private ArraySet<Integer> createAppidWhitelist(Context context) {
+ // Use a local copy instead of mAppIdWhitelist to prevent concurrent read access.
+ final ArraySet<Integer> whitelist = new ArraySet<>();
+
+ // We trust our own process.
+ whitelist.add(UserHandle.getAppId(Process.myUid()));
+ // We only need to initialize it once. UPDATE_DEVICE_STATS is a system permission.
+ final PackageManager pm = context.getPackageManager();
+ final String[] permissions = { android.Manifest.permission.UPDATE_DEVICE_STATS };
+ final int queryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+ final List<PackageInfo> packages =
+ pm.getPackagesHoldingPermissions(permissions, queryFlags);
+ final int packagesSize = packages.size();
+ for (int i = 0; i < packagesSize; i++) {
+ final PackageInfo pkgInfo = packages.get(i);
+ try {
+ final int uid = pm.getPackageUid(pkgInfo.packageName, queryFlags);
+ final int appId = UserHandle.getAppId(uid);
+ whitelist.add(appId);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Cannot find uid for package name " + pkgInfo.packageName, e);
+ }
+ }
+ return whitelist;
+ }
+ }
/** Listens for flag changes. */
private static class SettingsObserver extends ContentObserver {
@@ -63,13 +132,16 @@
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final BinderCallsStats mBinderCallsStats;
+ private final AuthorizedWorkSourceProvider mWorkSourceProvider;
- public SettingsObserver(Context context, BinderCallsStats binderCallsStats) {
+ SettingsObserver(Context context, BinderCallsStats binderCallsStats,
+ AuthorizedWorkSourceProvider workSourceProvider) {
super(BackgroundThread.getHandler());
mContext = context;
context.getContentResolver().registerContentObserver(mUri, false, this,
UserHandle.USER_SYSTEM);
mBinderCallsStats = binderCallsStats;
+ mWorkSourceProvider = workSourceProvider;
// Always kick once to ensure that we match current state
onChange();
}
@@ -98,7 +170,7 @@
mBinderCallsStats.setSamplingInterval(mParser.getInt(
SETTINGS_SAMPLING_INTERVAL_KEY,
BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
- mBinderCallsStats.setSamplingInterval(mParser.getInt(
+ mBinderCallsStats.setMaxBinderCallStats(mParser.getInt(
SETTINGS_MAX_CALL_STATS_KEY,
BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT));
@@ -110,12 +182,15 @@
Binder.setObserver(mBinderCallsStats);
Binder.setProxyTransactListener(
new Binder.PropagateWorkSourceTransactListener());
+ Binder.setWorkSourceProvider(mWorkSourceProvider);
} else {
Binder.setObserver(null);
Binder.setProxyTransactListener(null);
+ Binder.setWorkSourceProvider(Binder::getCallingUid);
}
mEnabled = enabled;
mBinderCallsStats.reset();
+ mBinderCallsStats.setAddDebugEntries(enabled);
}
}
}
@@ -154,6 +229,7 @@
public static class LifeCycle extends SystemService {
private BinderCallsStatsService mService;
private BinderCallsStats mBinderCallsStats;
+ private AuthorizedWorkSourceProvider mWorkSourceProvider;
public LifeCycle(Context context) {
super(context);
@@ -162,7 +238,9 @@
@Override
public void onStart() {
mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
- mService = new BinderCallsStatsService(mBinderCallsStats);
+ mWorkSourceProvider = new AuthorizedWorkSourceProvider();
+ mService = new BinderCallsStatsService(
+ mBinderCallsStats, mWorkSourceProvider);
publishLocalService(Internal.class, new Internal(mBinderCallsStats));
publishBinderService("binder_calls_stats", mService);
boolean detailedTrackingEnabled = SystemProperties.getBoolean(
@@ -181,21 +259,27 @@
if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
CachedDeviceState.Readonly deviceState = getLocalService(
CachedDeviceState.Readonly.class);
- mService.systemReady(getContext());
mBinderCallsStats.setDeviceState(deviceState);
+ // It needs to be called before mService.systemReady to make sure the observer is
+ // initialized before installing it.
+ mWorkSourceProvider.systemReady(getContext());
+ mService.systemReady(getContext());
}
}
}
private SettingsObserver mSettingsObserver;
private final BinderCallsStats mBinderCallsStats;
+ private final AuthorizedWorkSourceProvider mWorkSourceProvider;
- BinderCallsStatsService(BinderCallsStats binderCallsStats) {
+ BinderCallsStatsService(BinderCallsStats binderCallsStats,
+ AuthorizedWorkSourceProvider workSourceProvider) {
mBinderCallsStats = binderCallsStats;
+ mWorkSourceProvider = workSourceProvider;
}
public void systemReady(Context context) {
- mSettingsObserver = new SettingsObserver(context, mBinderCallsStats);
+ mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mWorkSourceProvider);
}
public void reset() {
@@ -233,6 +317,9 @@
mBinderCallsStats.setDetailedTracking(false);
pw.println("Detailed tracking disabled");
return;
+ } else if ("--dump-worksource-provider".equals(arg)) {
+ mWorkSourceProvider.dump(pw, AppIdToPackageMap.getSnapshot());
+ return;
} else if ("-h".equals(arg)) {
pw.println("binder_calls_stats commands:");
pw.println(" --reset: Reset stats");
@@ -247,29 +334,6 @@
}
}
}
- mBinderCallsStats.dump(pw, getAppIdToPackagesMap(), verbose);
+ mBinderCallsStats.dump(pw, AppIdToPackageMap.getSnapshot(), verbose);
}
-
- private Map<Integer, String> getAppIdToPackagesMap() {
- List<PackageInfo> packages;
- try {
- packages = AppGlobals.getPackageManager()
- .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES,
- UserHandle.USER_SYSTEM).getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- Map<Integer,String> map = new HashMap<>();
- for (PackageInfo pkg : packages) {
- String name = pkg.packageName;
- int uid = pkg.applicationInfo.uid;
- // Use sharedUserId string as package name if there are collisions
- if (pkg.sharedUserId != null && map.containsKey(uid)) {
- name = "shared:" + pkg.sharedUserId;
- }
- map.put(uid, name);
- }
- return map;
- }
-
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bb3b9f7..eda9fe1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -274,7 +274,8 @@
private int mDefaultInetConditionPublished = 0;
private INetworkManagementService mNMS;
- private INetd mNetd;
+ @VisibleForTesting
+ protected INetd mNetd;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -901,6 +902,7 @@
// Listen to package add and removal events for all users.
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
mContext.registerReceiverAsUser(
@@ -2090,7 +2092,8 @@
}
}
- private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
+ @VisibleForTesting
+ protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
// Overridden for testing purposes to avoid writing to SystemProperties.
@@ -2116,15 +2119,11 @@
try {
if (VDBG || DDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
- final String prefix = "/sys/kernel/ipv4/tcp_";
- FileUtils.stringToFile(prefix + "rmem_min", values[0]);
- FileUtils.stringToFile(prefix + "rmem_def", values[1]);
- FileUtils.stringToFile(prefix + "rmem_max", values[2]);
- FileUtils.stringToFile(prefix + "wmem_min", values[3]);
- FileUtils.stringToFile(prefix + "wmem_def", values[4]);
- FileUtils.stringToFile(prefix + "wmem_max", values[5]);
+ String rmemValues = String.join(" ", values[0], values[1], values[2]);
+ String wmemValues = String.join(" ", values[3], values[4], values[5]);
+ mNetd.setTcpRWmemorySize(rmemValues, wmemValues);
mCurrentTcpBufferSizes = tcpBufferSizes;
- } catch (IOException e) {
+ } catch (RemoteException | ServiceSpecificException e) {
loge("Can't set TCP buffer sizes:" + e);
}
@@ -4205,12 +4204,46 @@
mPermissionMonitor.onPackageAdded(packageName, uid);
}
- private void onPackageRemoved(String packageName, int uid) {
+ private void onPackageReplaced(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
+ return;
+ }
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ return;
+ }
+ // Legacy always-on VPN won't be affected since the package name is not set.
+ if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
+ Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+ + userId);
+ vpn.startAlwaysOnVpn();
+ }
+ }
+ }
+
+ private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
return;
}
mPermissionMonitor.onPackageRemoved(uid);
+
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ return;
+ }
+ // Legacy always-on VPN won't be affected since the package name is not set.
+ if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
+ Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ + userId);
+ vpn.setAlwaysOnPackage(null, false);
+ }
+ }
}
private void onUserUnlocked(int userId) {
@@ -4247,8 +4280,12 @@
onUserUnlocked(userId);
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
onPackageAdded(packageName, uid);
+ } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ onPackageReplaced(packageName, uid);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- onPackageRemoved(packageName, uid);
+ final boolean isReplacing = intent.getBooleanExtra(
+ Intent.EXTRA_REPLACING, false);
+ onPackageRemoved(packageName, uid, isReplacing);
}
}
};
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index af9d4c8..08cb7a2 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -43,6 +43,7 @@
import android.net.INetworkPolicyManager;
import android.net.NetworkInfo;
import android.net.Uri;
+import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
@@ -272,6 +273,7 @@
private PowerManager mPowerManager;
private INetworkPolicyManager mNetworkPolicyManager;
private SensorManager mSensorManager;
+ private final boolean mUseMotionSensor;
private Sensor mMotionSensor;
private LocationRequest mLocationRequest;
private Intent mIdleIntent;
@@ -520,9 +522,10 @@
updateConnectivityState(intent);
} break;
case Intent.ACTION_BATTERY_CHANGED: {
+ boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
+ boolean plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
synchronized (DeviceIdleController.this) {
- int plugged = intent.getIntExtra("plugged", 0);
- updateChargingLocked(plugged != 0);
+ updateChargingLocked(present && plugged);
}
} break;
case Intent.ACTION_PACKAGE_REMOVED: {
@@ -1629,6 +1632,9 @@
mHandler = mInjector.getHandler(this);
mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
+
+ mUseMotionSensor = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
}
public DeviceIdleController(Context context) {
@@ -1729,20 +1735,23 @@
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
- int sigMotionSensorId = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
- if (sigMotionSensorId > 0) {
- mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
- }
- if (mMotionSensor == null && getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
- mMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_WRIST_TILT_GESTURE, true);
- }
- if (mMotionSensor == null) {
- // As a last ditch, fall back to SMD.
- mMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_SIGNIFICANT_MOTION, true);
+
+ if (mUseMotionSensor) {
+ int sigMotionSensorId = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
+ if (sigMotionSensorId > 0) {
+ mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+ }
+ if (mMotionSensor == null && getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_WRIST_TILT_GESTURE, true);
+ }
+ if (mMotionSensor == null) {
+ // As a last ditch, fall back to SMD.
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_SIGNIFICANT_MOTION, true);
+ }
}
if (getContext().getResources().getBoolean(
@@ -2588,14 +2597,21 @@
mState = STATE_SENSING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
EventLogTags.writeDeviceIdle(mState, reason);
- scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
cancelLocatingLocked();
- mNotMoving = false;
mLocated = false;
mLastGenericLocation = null;
mLastGpsLocation = null;
- mAnyMotionDetector.checkForAnyMotion();
- break;
+
+ // If we have an accelerometer, wait to find out whether we are moving.
+ if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {
+ scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
+ mNotMoving = false;
+ mAnyMotionDetector.checkForAnyMotion();
+ break;
+ }
+
+ mNotMoving = true;
+ // Otherwise, fall through and check this off the list of requirements.
case STATE_SENSING:
cancelSensingTimeoutAlarmLocked();
mState = STATE_LOCATING;
@@ -2893,9 +2909,12 @@
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
- if (mMotionSensor == null && !(mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
- || mState == STATE_IDLE_MAINTENANCE)) {
- // If there is no motion sensor on this device, then we won't schedule
+
+ if (mUseMotionSensor && mMotionSensor == null
+ && mState != STATE_QUICK_DOZE_DELAY
+ && mState != STATE_IDLE
+ && mState != STATE_IDLE_MAINTENANCE) {
+ // If there is no motion sensor on this device, but we need one, then we won't schedule
// alarms, because we can't determine if the device is not moving. This effectively
// turns off normal execution of device idling, although it is still possible to
// manually poke it by pretending like the alarm is going off.
@@ -3765,13 +3784,20 @@
pw.print(" mLightEnabled="); pw.print(mLightEnabled);
pw.print(" mDeepEnabled="); pw.println(mDeepEnabled);
pw.print(" mForceIdle="); pw.println(mForceIdle);
- pw.print(" mMotionSensor="); pw.println(mMotionSensor);
+ pw.print(" mUseMotionSensor="); pw.print(mUseMotionSensor);
+ if (mUseMotionSensor) {
+ pw.print(" mMotionSensor="); pw.println(mMotionSensor);
+ } else {
+ pw.println();
+ }
pw.print(" mScreenOn="); pw.println(mScreenOn);
pw.print(" mScreenLocked="); pw.println(mScreenLocked);
pw.print(" mNetworkConnected="); pw.println(mNetworkConnected);
pw.print(" mCharging="); pw.println(mCharging);
pw.print(" mMotionActive="); pw.println(mMotionListener.active);
- pw.print(" mNotMoving="); pw.println(mNotMoving);
+ if (mUseMotionSensor) {
+ pw.print(" mNotMoving="); pw.println(mNotMoving);
+ }
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
pw.print(mHasGps); pw.print(" mHasNetwork=");
pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 7ee3d3b..126bf65 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1236,7 +1236,8 @@
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
- mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
+ mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
+ new ParcelFileDescriptor(sockFd), callingUid);
if (port != 0) {
Log.v(TAG, "Binding to port " + port);
Os.bind(sockFd, INADDR_ANY, port);
@@ -1696,7 +1697,7 @@
mSrvConfig
.getNetdInstance()
.ipSecApplyTransportModeTransform(
- socket.getFileDescriptor(),
+ socket,
callingUid,
direction,
c.getSourceAddress(),
@@ -1715,7 +1716,7 @@
throws RemoteException {
mSrvConfig
.getNetdInstance()
- .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
+ .ipSecRemoveTransportModeTransform(socket);
}
/**
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cc7bf33..8b992eb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,8 +17,11 @@
package com.android.server;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationProvider.AVAILABLE;
import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
+import static com.android.internal.util.Preconditions.checkState;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -47,14 +50,12 @@
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
import android.location.IGpsGeofenceHardware;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
-import android.location.LocationProvider;
import android.location.LocationRequest;
import android.os.Binder;
import android.os.Bundle;
@@ -84,6 +85,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
@@ -92,9 +94,9 @@
import com.android.server.location.GnssLocationProvider;
import com.android.server.location.GnssMeasurementsProvider;
import com.android.server.location.GnssNavigationMessageProvider;
+import com.android.server.location.GnssStatusListenerHelper;
import com.android.server.location.LocationBlacklist;
import com.android.server.location.LocationFudger;
-import com.android.server.location.LocationProviderInterface;
import com.android.server.location.LocationProviderProxy;
import com.android.server.location.LocationRequestStatistics;
import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
@@ -131,8 +133,6 @@
// Location resolution level: fine location data
private static final int RESOLUTION_LEVEL_FINE = 2;
- private static final String ACCESS_MOCK_LOCATION =
- android.Manifest.permission.ACCESS_MOCK_LOCATION;
private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
private static final String INSTALL_LOCATION_PROVIDER =
@@ -182,7 +182,7 @@
private ActivityManager mActivityManager;
private UserManager mUserManager;
private GeocoderProxy mGeocodeProvider;
- private IGnssStatusProvider mGnssStatusProvider;
+ private GnssStatusListenerHelper mGnssStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
@@ -192,12 +192,6 @@
private IGpsGeofenceHardware mGpsGeofenceProxy;
// --- fields below are protected by mLock ---
- // Set of providers that are explicitly enabled
- // Only used by passive, fused & test. Network & GPS are controlled separately, and not listed.
- private final Set<String> mEnabledProviders = new HashSet<>();
-
- // Set of providers that are explicitly disabled
- private final Set<String> mDisabledProviders = new HashSet<>();
// Mock (test) providers
private final HashMap<String, MockProvider> mMockProviders =
@@ -207,15 +201,15 @@
private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
// currently installed providers (with mocks replacing real providers)
- private final ArrayList<LocationProviderInterface> mProviders =
+ private final ArrayList<LocationProvider> mProviders =
new ArrayList<>();
// real providers, saved here when mocked out
- private final HashMap<String, LocationProviderInterface> mRealProviders =
+ private final HashMap<String, LocationProvider> mRealProviders =
new HashMap<>();
// mapping from provider name to provider
- private final HashMap<String, LocationProviderInterface> mProvidersByName =
+ private final HashMap<String, LocationProvider> mProvidersByName =
new HashMap<>();
// mapping from provider name to all its UpdateRecords
@@ -270,13 +264,8 @@
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
packageManagerInternal.setLocationPackagesProvider(
- new PackageManagerInternal.PackagesProvider() {
- @Override
- public String[] getPackages(int userId) {
- return mContext.getResources().getStringArray(
- com.android.internal.R.array.config_locationProviderPackageNames);
- }
- });
+ userId -> mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationProviderPackageNames));
if (D) Log.d(TAG, "Constructed");
@@ -321,30 +310,17 @@
mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
- PackageManager.OnPermissionsChangedListener permissionListener
- = new PackageManager.OnPermissionsChangedListener() {
- @Override
- public void onPermissionsChanged(final int uid) {
- synchronized (mLock) {
- applyAllProviderRequirementsLocked();
- }
+ PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+ synchronized (mLock) {
+ applyAllProviderRequirementsLocked();
}
};
mPackageManager.addOnPermissionsChangeListener(permissionListener);
// listen for background/foreground changes
- ActivityManager.OnUidImportanceListener uidImportanceListener
- = new ActivityManager.OnUidImportanceListener() {
- @Override
- public void onUidImportance(final int uid, final int importance) {
- mLocationHandler.post(new Runnable() {
- @Override
- public void run() {
- onUidImportanceChanged(uid, importance);
- }
- });
- }
- };
+ ActivityManager.OnUidImportanceListener uidImportanceListener =
+ (uid, importance) -> mLocationHandler.post(
+ () -> onUidImportanceChanged(uid, importance));
mActivityManager.addOnUidImportanceListener(uidImportanceListener,
FOREGROUND_IMPORTANCE_CUTOFF);
@@ -356,7 +332,10 @@
// prepare providers
loadProvidersLocked();
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
// listen for settings changes
@@ -366,7 +345,7 @@
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
}
}
}, UserHandle.USER_ALL);
@@ -377,7 +356,9 @@
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
- updateProvidersLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
}
}, UserHandle.USER_ALL);
@@ -402,7 +383,9 @@
public void onChange(boolean selfChange) {
synchronized (mLock) {
updateBackgroundThrottlingWhitelistLocked();
- updateProvidersLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
}
}, UserHandle.USER_ALL);
@@ -414,7 +397,6 @@
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- intentFilter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
@@ -425,12 +407,6 @@
} else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
|| Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
updateUserProfiles(mCurrentUserId);
- } else if (Intent.ACTION_SHUTDOWN.equals(action)) {
- // shutdown only if UserId indicates whole system, not just one user
- if (D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
- if (getSendingUserId() == UserHandle.USER_ALL) {
- shutdownComponents();
- }
}
}
}, UserHandle.ALL, intentFilter, null, mLocationHandler);
@@ -463,14 +439,16 @@
}
for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
- if (entry.getValue().mUid == uid) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
if (D) {
Log.d(TAG, "gnss measurements listener from uid " + uid
+ " is now " + (foreground ? "foreground" : "background)"));
}
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssMeasurementsProvider.addListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+ IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
} else {
mGnssMeasurementsProvider.removeListener(
IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
@@ -479,7 +457,8 @@
}
for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
- if (entry.getValue().mUid == uid) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
if (D) {
Log.d(TAG, "gnss navigation message listener from uid "
+ uid + " is now "
@@ -487,13 +466,16 @@
}
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssNavigationMessageProvider.addListener(
- IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+ IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
} else {
mGnssNavigationMessageProvider.removeListener(
IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
}
}
}
+
+ // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
}
}
@@ -502,30 +484,13 @@
}
/**
- * Provides a way for components held by the {@link LocationManagerService} to clean-up
- * gracefully on system's shutdown.
- *
- * NOTES:
- * 1) Only provides a chance to clean-up on an opt-in basis. This guarantees back-compat
- * support for components that do not wish to handle such event.
- */
- private void shutdownComponents() {
- if (D) Log.d(TAG, "Shutting down components...");
-
- LocationProviderInterface gpsProvider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
- if (gpsProvider != null && gpsProvider.isEnabled()) {
- gpsProvider.disable();
- }
- }
-
- /**
* Makes a list of userids that are related to the current user. This is
* relevant when using managed profiles. Otherwise the list only contains
* the current user.
*
* @param currentUserId the current user, who might have an alter-ego.
*/
- void updateUserProfiles(int currentUserId) {
+ private void updateUserProfiles(int currentUserId) {
int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
synchronized (mLock) {
mCurrentUserProfiles = profileIds;
@@ -614,22 +579,28 @@
private void loadProvidersLocked() {
// create a passive location provider, which is always enabled
- PassiveProvider passiveProvider = new PassiveProvider(this);
- addProviderLocked(passiveProvider);
- mEnabledProviders.add(passiveProvider.getName());
+ LocationProvider passiveProviderManager = new LocationProvider(
+ LocationManager.PASSIVE_PROVIDER);
+ PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
+
+ addProviderLocked(passiveProviderManager);
mPassiveProvider = passiveProvider;
if (GnssLocationProvider.isSupported()) {
// Create a gps location provider
- GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
+ LocationProvider gnssProviderManager = new LocationProvider(
+ LocationManager.GPS_PROVIDER);
+ GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
+ gnssProviderManager,
mLocationHandler.getLooper());
+
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
- addProviderLocked(gnssProvider);
- mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
+ addProviderLocked(gnssProviderManager);
+ mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
@@ -657,34 +628,38 @@
ensureFallbackFusedProviderPresentLocked(pkgs);
// bind to network provider
+
+ LocationProvider networkProviderManager = new LocationProvider(
+ LocationManager.NETWORK_PROVIDER);
LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
mContext,
- LocationManager.NETWORK_PROVIDER,
+ networkProviderManager,
NETWORK_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableNetworkLocationOverlay,
com.android.internal.R.string.config_networkLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (networkProvider != null) {
- mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+ mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
mProxyProviders.add(networkProvider);
- addProviderLocked(networkProvider);
+ addProviderLocked(networkProviderManager);
} else {
Slog.w(TAG, "no network location provider found");
}
// bind to fused provider
- LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+ LocationProvider fusedProviderManager = new LocationProvider(
+ LocationManager.FUSED_PROVIDER);
+ LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
mContext,
- LocationManager.FUSED_PROVIDER,
+ fusedProviderManager,
FUSED_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
- if (fusedLocationProvider != null) {
- addProviderLocked(fusedLocationProvider);
- mProxyProviders.add(fusedLocationProvider);
- mEnabledProviders.add(fusedLocationProvider.getName());
- mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
+ if (fusedProvider != null) {
+ addProviderLocked(fusedProviderManager);
+ mProxyProviders.add(fusedProvider);
+ mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
} else {
Slog.e(TAG, "no fused location provider found",
new IllegalStateException("Location service needs a fused location provider"));
@@ -765,12 +740,9 @@
synchronized (mLock) {
mLastLocation.clear();
mLastLocationCoarseInterval.clear();
- for (LocationProviderInterface p : mProviders) {
- updateProviderListenersLocked(p.getName(), false);
- }
- mCurrentUserId = userId;
updateUserProfiles(userId);
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
+ mCurrentUserId = userId;
}
}
@@ -786,6 +758,165 @@
}
}
+ private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
+
+ private final String mName;
+ private AbstractLocationProvider mProvider;
+
+ // whether the provider is enabled in location settings
+ private boolean mSettingsEnabled;
+
+ // whether the provider considers itself enabled
+ private volatile boolean mEnabled;
+
+ @Nullable
+ private volatile ProviderProperties mProperties;
+
+ private LocationProvider(String name) {
+ mName = name;
+ // TODO: initialize settings enabled?
+ }
+
+ @Override
+ public void onAttachProvider(AbstractLocationProvider provider, boolean initiallyEnabled) {
+ checkState(mProvider == null);
+
+ // the provider is not yet fully constructed at this point, so we may not do anything
+ // except save a reference for later use here. do not call any provider methods.
+ mProvider = provider;
+ mEnabled = initiallyEnabled;
+ mProperties = null;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean isEnabled() {
+ return mSettingsEnabled && mEnabled;
+ }
+
+ @Nullable
+ public ProviderProperties getProperties() {
+ return mProperties;
+ }
+
+ public void setRequest(ProviderRequest request, WorkSource workSource) {
+ mProvider.setRequest(request, workSource);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(mName + " provider:");
+ pw.println(" setting=" + mSettingsEnabled);
+ pw.println(" enabled=" + mEnabled);
+ pw.println(" properties=" + mProperties);
+ mProvider.dump(fd, pw, args);
+ }
+
+ public long getStatusUpdateTime() {
+ return mProvider.getStatusUpdateTime();
+ }
+
+ public int getStatus(Bundle extras) {
+ return mProvider.getStatus(extras);
+ }
+
+ public void sendExtraCommand(String command, Bundle extras) {
+ mProvider.sendExtraCommand(command, extras);
+ }
+
+ // called from any thread
+ @Override
+ public void onReportLocation(Location location) {
+ runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+ mProvider == mPassiveProvider));
+ }
+
+ // called from any thread
+ @Override
+ public void onReportLocation(List<Location> locations) {
+ runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+ }
+
+ // called from any thread
+ @Override
+ public void onSetEnabled(boolean enabled) {
+ runOnHandler(() -> {
+ if (enabled == mEnabled) {
+ return;
+ }
+
+ mEnabled = enabled;
+
+ if (!mSettingsEnabled) {
+ // this provider was disabled in settings anyways, so a change to it's own
+ // enabled status won't have any affect.
+ return;
+ }
+
+ // traditionally clients can listen for changes to the LOCATION_PROVIDERS_ALLOWED
+ // setting to detect when providers are enabled or disabled (even though they aren't
+ // supposed to). to continue to support this we must force a change to this setting.
+ // we use the fused provider because this is forced to be always enabled in settings
+ // anyways, and so won't have any visible effect beyond triggering content observers
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ "+" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+
+ synchronized (mLock) {
+ if (!enabled) {
+ // If any provider has been disabled, clear all last locations for all
+ // providers. This is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ }
+
+ updateProviderListenersLocked(mName);
+ }
+
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+ UserHandle.ALL);
+ });
+ }
+
+ @Override
+ public void onSetProperties(ProviderProperties properties) {
+ runOnHandler(() -> mProperties = properties);
+ }
+
+ private void setSettingsEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (mSettingsEnabled == enabled) {
+ return;
+ }
+
+ mSettingsEnabled = enabled;
+ if (!mSettingsEnabled) {
+ // if any provider has been disabled, clear all last locations for all
+ // providers. this is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateProviderListenersLocked(mName);
+ } else if (mEnabled) {
+ updateProviderListenersLocked(mName);
+ }
+ }
+ }
+
+ private void runOnHandler(Runnable runnable) {
+ if (Looper.myLooper() == mLocationHandler.getLooper()) {
+ runnable.run();
+ } else {
+ mLocationHandler.post(runnable);
+ }
+ }
+ }
+
/**
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
@@ -793,24 +924,24 @@
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
final Identity mIdentity;
- final int mAllowedResolutionLevel; // resolution level allowed to receiver
+ private final int mAllowedResolutionLevel; // resolution level allowed to receiver
- final ILocationListener mListener;
+ private final ILocationListener mListener;
final PendingIntent mPendingIntent;
final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
- final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
- final Object mKey;
+ private final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
+ private final Object mKey;
final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>();
// True if app ops has started monitoring this receiver for locations.
- boolean mOpMonitoring;
+ private boolean mOpMonitoring;
// True if app ops has started monitoring this receiver for high power (gps) locations.
- boolean mOpHighPowerMonitoring;
- int mPendingBroadcasts;
+ private boolean mOpHighPowerMonitoring;
+ private int mPendingBroadcasts;
PowerManager.WakeLock mWakeLock;
- Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+ private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
String packageName, WorkSource workSource, boolean hideFromAppOps) {
mListener = listener;
mPendingIntent = intent;
@@ -885,9 +1016,10 @@
// See if receiver has any enabled update records. Also note if any update records
// are high power (has a high power provider with an interval under a threshold).
for (UpdateRecord updateRecord : mUpdateRecords.values()) {
- if (isAllowedByCurrentUserSettingsLocked(updateRecord.mProvider)) {
+ if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+ mCurrentUserId)) {
requestingLocation = true;
- LocationProviderInterface locationProvider
+ LocationManagerService.LocationProvider locationProvider
= mProvidersByName.get(updateRecord.mProvider);
ProviderProperties properties = locationProvider != null
? locationProvider.getProperties() : null;
@@ -1034,7 +1166,7 @@
return true;
}
- public boolean callProviderEnabledLocked(String provider, boolean enabled) {
+ private boolean callProviderEnabledLocked(String provider, boolean enabled) {
// First update AppOp monitoring.
// An app may get/lose location access as providers are enabled/disabled.
updateMonitoring(true);
@@ -1236,7 +1368,7 @@
private class LinkedCallback implements IBinder.DeathRecipient {
private final IBatchedLocationCallback mCallback;
- public LinkedCallback(@NonNull IBatchedLocationCallback callback) {
+ private LinkedCallback(@NonNull IBatchedLocationCallback callback) {
mCallback = callback;
}
@@ -1337,7 +1469,7 @@
checkCallerIsProvider();
// Currently used only for GNSS locations - update permissions check if changed
- if (isAllowedByCurrentUserSettingsLocked(LocationManager.GPS_PROVIDER)) {
+ if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
if (mGnssBatchingCallback == null) {
Slog.e(TAG, "reportLocationBatch() called without active Callback");
return;
@@ -1352,29 +1484,17 @@
}
}
- private void addProviderLocked(LocationProviderInterface provider) {
+ private void addProviderLocked(LocationProvider provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProviderLocked(LocationProviderInterface provider) {
- provider.disable();
+ private void removeProviderLocked(LocationProvider provider) {
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
/**
- * Returns "true" if access to the specified location provider is allowed by the current
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- */
- private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
- return isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId);
- }
-
- /**
* Returns "true" if access to the specified location provider is allowed by the specified
* user's settings. Access to all location providers is forbidden to non-location-provider
* processes belonging to background users.
@@ -1383,13 +1503,28 @@
* @param userId the user id to query
*/
private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
- if (mEnabledProviders.contains(provider)) {
- return true;
+ if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+ return isLocationEnabledForUser(userId);
}
- if (mDisabledProviders.contains(provider)) {
- return false;
+ if (LocationManager.FUSED_PROVIDER.equals(provider)) {
+ return isLocationEnabledForUser(userId);
}
- return isLocationProviderEnabledForUser(provider, userId);
+ synchronized (mLock) {
+ if (mMockProviders.containsKey(provider)) {
+ return isLocationEnabledForUser(userId);
+ }
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // Use system settings
+ ContentResolver cr = mContext.getContentResolver();
+ String allowedProviders = Settings.Secure.getStringForUser(
+ cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+ return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@@ -1481,9 +1616,11 @@
// network and fused providers are ok with COARSE or FINE
return RESOLUTION_LEVEL_COARSE;
} else {
- // mock providers
- LocationProviderInterface lp = mMockProviders.get(provider);
- if (lp != null) {
+ for (LocationProvider lp : mProviders) {
+ if (!lp.getName().equals(provider)) {
+ continue;
+ }
+
ProviderProperties properties = lp.getProperties();
if (properties != null) {
if (properties.mRequiresSatellite) {
@@ -1496,6 +1633,7 @@
}
}
}
+
return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
}
@@ -1550,7 +1688,7 @@
}
private static String resolutionLevelToOpStr(int allowedResolutionLevel) {
- switch(allowedResolutionLevel) {
+ switch (allowedResolutionLevel) {
case RESOLUTION_LEVEL_COARSE:
return AppOpsManager.OPSTR_COARSE_LOCATION;
case RESOLUTION_LEVEL_FINE:
@@ -1565,7 +1703,7 @@
}
}
- boolean reportLocationAccessNoThrow(
+ private boolean reportLocationAccessNoThrow(
int pid, int uid, String packageName, int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
@@ -1577,7 +1715,8 @@
return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
}
- boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
+ private boolean checkLocationAccess(int pid, int uid, String packageName,
+ int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
@@ -1597,7 +1736,7 @@
ArrayList<String> out;
synchronized (mLock) {
out = new ArrayList<>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
+ for (LocationProvider provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
@@ -1623,7 +1762,7 @@
try {
synchronized (mLock) {
out = new ArrayList<>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
+ for (LocationProvider provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
@@ -1633,7 +1772,8 @@
&& !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
continue;
}
- if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+ if (criteria != null
+ && !android.location.LocationProvider.propertiesMeetCriteria(
name, provider.getProperties(), criteria)) {
continue;
}
@@ -1658,7 +1798,7 @@
*/
@Override
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- String result = null;
+ String result;
List<String> providers = getProviders(criteria, enabledOnly);
if (!providers.isEmpty()) {
@@ -1673,7 +1813,7 @@
return result;
}
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
return null;
}
@@ -1689,51 +1829,32 @@
@Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
}
- boolean result = LocationProvider.propertiesMeetCriteria(
+ boolean result = android.location.LocationProvider.propertiesMeetCriteria(
p.getName(), p.getProperties(), criteria);
if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
return result;
}
- private void updateProvidersLocked() {
- boolean changesMade = false;
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- boolean isEnabled = p.isEnabled();
- String name = p.getName();
- boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
- if (isEnabled && !shouldBeEnabled) {
- updateProviderListenersLocked(name, false);
- // If any provider has been disabled, clear all last locations for all providers.
- // This is to be on the safe side in case a provider has location derived from
- // this disabled provider.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- changesMade = true;
- } else if (!isEnabled && shouldBeEnabled) {
- updateProviderListenersLocked(name, true);
- changesMade = true;
- }
+ private void updateProvidersSettingsLocked() {
+ for (LocationProvider p : mProviders) {
+ p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
}
- if (changesMade) {
- mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
- UserHandle.ALL);
- mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
- UserHandle.ALL);
- }
+
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
+ UserHandle.ALL);
}
- private void updateProviderListenersLocked(String provider, boolean enabled) {
- int listeners = 0;
-
- LocationProviderInterface p = mProvidersByName.get(provider);
+ private void updateProviderListenersLocked(String provider) {
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
+ boolean enabled = p.isEnabled();
+
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1747,7 +1868,6 @@
}
deadReceivers.add(record.mReceiver);
}
- listeners++;
}
}
}
@@ -1758,16 +1878,11 @@
}
}
- if (enabled) {
- p.enable();
- applyRequirementsLocked(provider);
- } else {
- p.disable();
- }
+ applyRequirementsLocked(provider);
}
private void applyRequirementsLocked(String provider) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1779,10 +1894,10 @@
resolver,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
- // initialize the low power mode to true and set to false if any of the records requires
- providerRequest.lowPowerMode = true;
- if (records != null) {
+ if (p.isEnabled() && records != null && !records.isEmpty()) {
+ // initialize the low power mode to true and set to false if any of the records requires
+ providerRequest.lowPowerMode = true;
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
if (checkLocationAccess(
@@ -1875,7 +1990,7 @@
public String[] getBackgroundThrottlingWhitelist() {
synchronized (mLock) {
return mBackgroundThrottlePackageWhitelist.toArray(
- new String[mBackgroundThrottlePackageWhitelist.size()]);
+ new String[0]);
}
}
@@ -1922,17 +2037,17 @@
private class UpdateRecord {
final String mProvider;
- final LocationRequest mRealRequest; // original request from client
+ private final LocationRequest mRealRequest; // original request from client
LocationRequest mRequest; // possibly throttled version of the request
- final Receiver mReceiver;
- boolean mIsForegroundUid;
- Location mLastFixBroadcast;
- long mLastStatusBroadcast;
+ private final Receiver mReceiver;
+ private boolean mIsForegroundUid;
+ private Location mLastFixBroadcast;
+ private long mLastStatusBroadcast;
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+ private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
mProvider = provider;
mRealRequest = request;
mRequest = request;
@@ -1958,7 +2073,7 @@
/**
* Method to be called when record changes foreground/background
*/
- void updateForeground(boolean isForeground){
+ private void updateForeground(boolean isForeground) {
mIsForegroundUid = isForeground;
mRequestStatistics.updateForeground(
mReceiver.mIdentity.mPackageName, mProvider, isForeground);
@@ -1967,7 +2082,7 @@
/**
* Method to be called when a record will no longer be used.
*/
- void disposeLocked(boolean removeReceiver) {
+ private void disposeLocked(boolean removeReceiver) {
mRequestStatistics.stopRequesting(mReceiver.mIdentity.mPackageName, mProvider);
// remove from mRecordsByProvider
@@ -1980,13 +2095,11 @@
// remove from Receiver#mUpdateRecords
HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
- if (receiverRecords != null) {
- receiverRecords.remove(this.mProvider);
+ receiverRecords.remove(this.mProvider);
- // and also remove the Receiver if it has no more update records
- if (receiverRecords.size() == 0) {
- removeUpdatesLocked(mReceiver);
- }
+ // and also remove the Receiver if it has no more update records
+ if (receiverRecords.size() == 0) {
+ removeUpdatesLocked(mReceiver);
}
}
@@ -2070,7 +2183,7 @@
private void checkPackageName(String packageName) {
if (packageName == null) {
- throw new SecurityException("invalid package name: " + packageName);
+ throw new SecurityException("invalid package name: " + null);
}
int uid = Binder.getCallingUid();
String[] packages = mPackageManager.getPackagesForUid(uid);
@@ -2085,7 +2198,7 @@
private void checkPendingIntent(PendingIntent intent) {
if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + intent);
+ throw new IllegalArgumentException("invalid pending intent: " + null);
}
}
@@ -2137,7 +2250,7 @@
synchronized (mLock) {
Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
packageName, workSource, hideFromAppOps);
- requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
+ requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2145,7 +2258,7 @@
}
private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
- int pid, int uid, String packageName) {
+ int uid, String packageName) {
// Figure out the provider. Either its explicitly request (legacy use cases), or
// use the fused provider
if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2154,7 +2267,7 @@
throw new IllegalArgumentException("provider name must not be null");
}
- LocationProviderInterface provider = mProvidersByName.get(name);
+ LocationProvider provider = mProvidersByName.get(name);
if (provider == null) {
throw new IllegalArgumentException("provider doesn't exist: " + name);
}
@@ -2173,8 +2286,7 @@
oldRecord.disposeLocked(false);
}
- boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid, mCurrentUserId);
- if (isProviderEnabled) {
+ if (provider.isEnabled()) {
applyRequirementsLocked(name);
} else {
// Notify the listener that updates are currently disabled
@@ -2194,10 +2306,8 @@
final int uid = Binder.getCallingUid();
synchronized (mLock) {
- WorkSource workSource = null;
- boolean hideFromAppOps = false;
Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
- packageName, workSource, hideFromAppOps);
+ packageName, null, false);
// providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
@@ -2236,22 +2346,12 @@
// update provider
for (String provider : providers) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(provider)) {
- continue;
- }
-
applyRequirementsLocked(provider);
}
}
private void applyAllProviderRequirementsLocked() {
- for (LocationProviderInterface p : mProviders) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(p.getName())) {
- continue;
- }
-
+ for (LocationProvider p : mProviders) {
applyRequirementsLocked(p.getName());
}
}
@@ -2291,7 +2391,7 @@
// or use the fused provider
String name = request.getProvider();
if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProviderInterface provider = mProvidersByName.get(name);
+ LocationProvider provider = mProvidersByName.get(name);
if (provider == null) return null;
if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
@@ -2314,7 +2414,7 @@
location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
if ((locationAgeMs > mLastLocationMaxAgeMs)
&& (mAppOps.unsafeCheckOp(op, uid, packageName)
- == AppOpsManager.MODE_FOREGROUND)) {
+ == AppOpsManager.MODE_FOREGROUND)) {
return null;
}
@@ -2358,7 +2458,7 @@
}
return false;
}
- LocationProviderInterface p = null;
+ LocationProvider p = null;
String provider = location.getProvider();
if (provider != null) {
p = mProvidersByName.get(provider);
@@ -2370,7 +2470,7 @@
return false;
}
synchronized (mLock) {
- if (!isAllowedByCurrentUserSettingsLocked(provider)) {
+ if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
if (D) {
Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
}
@@ -2444,31 +2544,20 @@
}
}
-
@Override
public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) {
if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) {
return false;
}
- try {
- mGnssStatusProvider.registerGnssStatusCallback(callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "mGpsStatusProvider.registerGnssStatusCallback failed", e);
- return false;
- }
- return true;
+ // TODO(b/120449926): The GNSS status listeners should be handled similar to the GNSS
+ // measurements listeners.
+ return mGnssStatusProvider.addListener(callback, Binder.getCallingUid(), packageName);
}
@Override
public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
- synchronized (mLock) {
- try {
- mGnssStatusProvider.unregisterGnssStatusCallback(callback);
- } catch (Exception e) {
- Slog.e(TAG, "mGpsStatusProvider.unregisterGnssStatusCallback failed", e);
- }
- }
+ mGnssStatusProvider.removeListener(callback);
}
@Override
@@ -2481,13 +2570,15 @@
synchronized (mLock) {
Identity callerIdentity
= new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ // TODO(b/120481270): Register for client death notification and update map.
mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
try {
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
mActivityManager.getPackageImportance(packageName))) {
- return mGnssMeasurementsProvider.addListener(listener);
+ return mGnssMeasurementsProvider.addListener(listener,
+ callerIdentity.mUid, callerIdentity.mPackageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2499,11 +2590,13 @@
@Override
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
- if (mGnssMeasurementsProvider != null) {
- synchronized (mLock) {
- mGnssMeasurementsListeners.remove(listener.asBinder());
- mGnssMeasurementsProvider.removeListener(listener);
- }
+ if (mGnssMeasurementsProvider == null) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mGnssMeasurementsListeners.remove(listener.asBinder());
+ mGnssMeasurementsProvider.removeListener(listener);
}
}
@@ -2518,13 +2611,15 @@
synchronized (mLock) {
Identity callerIdentity
= new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ // TODO(b/120481270): Register for client death notification and update map.
mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
try {
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
mActivityManager.getPackageImportance(packageName))) {
- return mGnssNavigationMessageProvider.addListener(listener);
+ return mGnssNavigationMessageProvider.addListener(listener,
+ callerIdentity.mUid, callerIdentity.mPackageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2560,10 +2655,11 @@
}
synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return false;
- return p.sendExtraCommand(command, extras);
+ p.sendExtraCommand(command, extras);
+ return true;
}
}
@@ -2588,14 +2684,10 @@
*/
@Override
public ProviderProperties getProviderProperties(String provider) {
- if (mProvidersByName.get(provider) == null) {
- return null;
- }
-
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
- LocationProviderInterface p;
+ LocationProvider p;
synchronized (mLock) {
p = mProvidersByName.get(provider);
}
@@ -2611,25 +2703,25 @@
*/
@Override
public String getNetworkProviderPackage() {
- LocationProviderInterface p;
+ LocationProvider p;
synchronized (mLock) {
- if (mProvidersByName.get(LocationManager.NETWORK_PROVIDER) == null) {
- return null;
- }
p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
}
- if (p instanceof LocationProviderProxy) {
- return ((LocationProviderProxy) p).getConnectedPackageName();
+ if (p == null) {
+ return null;
+ }
+ if (p.mProvider instanceof LocationProviderProxy) {
+ return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
}
return null;
}
/**
- * Returns the current location enabled/disabled status for a user
+ * Returns the current location enabled/disabled status for a user
*
- * @param userId the id of the user
- * @return true if location is enabled
+ * @param userId the id of the user
+ * @return true if location is enabled
*/
@Override
public boolean isLocationEnabledForUser(int userId) {
@@ -2647,7 +2739,7 @@
return false;
}
final List<String> providerList = Arrays.asList(allowedProviders.split(","));
- for(String provider : mRealProviders.keySet()) {
+ for (String provider : mRealProviders.keySet()) {
if (provider.equals(LocationManager.PASSIVE_PROVIDER)
|| provider.equals(LocationManager.FUSED_PROVIDER)) {
continue;
@@ -2664,16 +2756,16 @@
}
/**
- * Enable or disable location for a user
+ * Enable or disable location for a user
*
- * @param enabled true to enable location, false to disable location
- * @param userId the id of the user
+ * @param enabled true to enable location, false to disable location
+ * @param userId the id of the user
*/
@Override
public void setLocationEnabledForUser(boolean enabled, int userId) {
mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
+ android.Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Requires WRITE_SECURE_SETTINGS permission");
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
checkInteractAcrossUsersPermission(userId);
@@ -2688,7 +2780,7 @@
allProvidersSet.addAll(allRealProviders);
// When disabling location, disable gps and network provider that could have been
// enabled by location mode api.
- if (enabled == false) {
+ if (!enabled) {
allProvidersSet.add(LocationManager.GPS_PROVIDER);
allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
}
@@ -2723,26 +2815,34 @@
}
/**
- * Returns the current enabled/disabled status of a location provider and user
+ * Returns the current enabled/disabled status of a location provider and user
*
- * @param provider name of the provider
- * @param userId the id of the user
- * @return true if the provider exists and is enabled
+ * @param providerName name of the provider
+ * @param userId the id of the user
+ * @return true if the provider exists and is enabled
*/
@Override
- public boolean isProviderEnabledForUser(String provider, int userId) {
+ public boolean isProviderEnabledForUser(String providerName, int userId) {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
checkInteractAcrossUsersPermission(userId);
+ if (!isLocationEnabledForUser(userId)) {
+ return false;
+ }
+
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
// so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+ if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
- int uid = Binder.getCallingUid();
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null
- && isAllowedByUserSettingsLocked(provider, uid, userId);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ LocationProvider provider;
+ synchronized (mLock) {
+ provider = mProvidersByName.get(providerName);
+ }
+ return provider != null && provider.isEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -2750,66 +2850,13 @@
* Enable or disable a single location provider.
*
* @param provider name of the provider
- * @param enabled true to enable the provider. False to disable the provider
- * @param userId the id of the user to set
+ * @param enabled true to enable the provider. False to disable the provider
+ * @param userId the id of the user to set
* @return true if the value was set, false on errors
*/
@Override
public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
-
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- // No such provider exists
- if (!mProvidersByName.containsKey(provider)) return false;
-
- // If it is a test provider, do not write to Settings.Secure
- if (mMockProviders.containsKey(provider)) {
- setTestProviderEnabled(provider, enabled);
- return true;
- }
-
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- String providerChange = (enabled ? "+" : "-") + provider;
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- providerChange, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Read location provider status from Settings.Secure
- *
- * @param provider the location provider to query
- * @param userId the user id to query
- * @return true if the provider is enabled
- */
- private boolean isLocationProviderEnabledForUser(String provider, int userId) {
- long identity = Binder.clearCallingIdentity();
- try {
- // Use system settings
- ContentResolver cr = mContext.getContentResolver();
- String allowedProviders = Settings.Secure.getStringForUser(
- cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
- return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ return false;
}
/**
@@ -2941,7 +2988,7 @@
long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
// Skip if the provider is unknown.
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
updateLastLocationLocked(location, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
@@ -3053,7 +3100,7 @@
LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
long prevStatusUpdateTime = r.mLastStatusBroadcast;
if ((newStatusUpdateTime > prevStatusUpdateTime)
- && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+ && (prevStatusUpdateTime != 0 || status != AVAILABLE)) {
r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
@@ -3098,8 +3145,8 @@
/**
* Updates last location with the given location
*
- * @param location new location to update
- * @param provider Location provider to update for
+ * @param location new location to update
+ * @param provider Location provider to update for
*/
private void updateLastLocationLocked(Location location, String provider) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
@@ -3154,13 +3201,11 @@
}
synchronized (mLock) {
- if (isAllowedByCurrentUserSettingsLocked(provider)) {
- if (!passive) {
- // notify passive provider of the new location
- mPassiveProvider.updateLocation(myLocation);
- }
- handleLocationChangedLocked(myLocation, passive);
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(myLocation);
}
+ handleLocationChangedLocked(myLocation, passive);
}
}
@@ -3245,13 +3290,12 @@
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)
|| LocationManager.FUSED_PROVIDER.equals(name)) {
- LocationProviderInterface p = mProvidersByName.get(name);
+ LocationProvider p = mProvidersByName.get(name);
if (p != null) {
removeProviderLocked(p);
}
}
addTestProviderLocked(name, properties);
- updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
}
@@ -3260,9 +3304,12 @@
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
- MockProvider provider = new MockProvider(name, this, properties);
+
+ LocationProvider provider = new LocationProvider(name);
+ MockProvider mockProvider = new MockProvider(provider, properties);
+
addProviderLocked(provider);
- mMockProviders.put(name, provider);
+ mMockProviders.put(name, mockProvider);
mLastLocation.put(name, null);
mLastLocationCoarseInterval.put(name, null);
}
@@ -3274,28 +3321,25 @@
}
synchronized (mLock) {
-
- // These methods can't be called after removing the test provider, so first make sure
- // we don't leave anything dangling.
- clearTestProviderEnabled(provider, opPackageName);
- clearTestProviderLocation(provider, opPackageName);
-
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- long identity = Binder.clearCallingIdentity();
- removeProviderLocked(mProvidersByName.get(provider));
- // reinstate real provider if available
- LocationProviderInterface realProvider = mRealProviders.get(provider);
- if (realProvider != null) {
- addProviderLocked(realProvider);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeProviderLocked(mProvidersByName.get(provider));
+
+ // reinstate real provider if available
+ LocationProvider realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mLastLocation.put(provider, null);
- mLastLocationCoarseInterval.put(provider, null);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
}
}
@@ -3325,23 +3369,11 @@
// clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
long identity = Binder.clearCallingIdentity();
- mockProvider.setLocation(mock);
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderLocation(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
+ try {
+ mockProvider.setLocation(mock);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mockProvider.clearLocation();
}
}
@@ -3350,36 +3382,6 @@
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- setTestProviderEnabled(provider, enabled);
- }
-
- /** Enable or disable a test location provider. */
- private void setTestProviderEnabled(String provider, boolean enabled) {
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- long identity = Binder.clearCallingIdentity();
- if (enabled) {
- mockProvider.enable();
- mEnabledProviders.add(provider);
- mDisabledProviders.remove(provider);
- } else {
- mockProvider.disable();
- mEnabledProviders.remove(provider);
- mDisabledProviders.add(provider);
- }
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderEnabled(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
@@ -3387,10 +3389,11 @@
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
long identity = Binder.clearCallingIdentity();
- mEnabledProviders.remove(provider);
- mDisabledProviders.remove(provider);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
+ try {
+ mockProvider.setEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -3450,10 +3453,11 @@
+ identity.mPackageName + ": " + isThrottlingExemptLocked(identity));
}
pw.println(" Overlay Provider Packages:");
- for (LocationProviderInterface provider : mProviders) {
- if (provider instanceof LocationProviderProxy) {
+ for (LocationProvider provider : mProviders) {
+ if (provider.mProvider instanceof LocationProviderProxy) {
pw.println(" " + provider.getName() + ": "
- + ((LocationProviderProxy) provider).getConnectedPackageName());
+ + ((LocationProviderProxy) provider.mProvider)
+ .getConnectedPackageName());
}
}
pw.println(" Historical Records by Provider:");
@@ -3479,25 +3483,12 @@
mGeofenceManager.dump(pw);
- if (mEnabledProviders.size() > 0) {
- pw.println(" Enabled Providers:");
- for (String i : mEnabledProviders) {
- pw.println(" " + i);
- }
-
- }
- if (mDisabledProviders.size() > 0) {
- pw.println(" Disabled Providers:");
- for (String i : mDisabledProviders) {
- pw.println(" " + i);
- }
- }
pw.append(" ");
mBlacklist.dump(pw);
if (mMockProviders.size() > 0) {
pw.println(" Mock Providers:");
for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
- i.getValue().dump(pw, " ");
+ i.getValue().dump(fd, pw, args);
}
}
@@ -3514,13 +3505,7 @@
if (args.length > 0 && "short".equals(args[0])) {
return;
}
- for (LocationProviderInterface provider : mProviders) {
- pw.print(provider.getName() + " Internal State");
- if (provider instanceof LocationProviderProxy) {
- LocationProviderProxy proxy = (LocationProviderProxy) provider;
- pw.print(" (" + proxy.getConnectedPackageName() + ")");
- }
- pw.println(":");
+ for (LocationProvider provider : mProviders) {
provider.dump(fd, pw, args);
}
if (mGnssBatchingInProgress) {
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index c563ad2..cee98c1 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -31,6 +31,7 @@
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.os.AppIdToPackageMap;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.CachedDeviceState;
import com.android.internal.os.LooperStats;
@@ -92,8 +93,11 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ AppIdToPackageMap packageMap = AppIdToPackageMap.getSnapshot();
pw.print("Start time: ");
pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStats.getStartTimeMillis()));
+ pw.print("On battery time (ms): ");
+ pw.println(mStats.getBatteryTimeMillis());
final List<LooperStats.ExportedEntry> entries = mStats.getEntries();
entries.sort(Comparator
.comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
@@ -119,7 +123,7 @@
pw.println(header);
for (LooperStats.ExportedEntry entry : entries) {
pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
- entry.workSourceUid,
+ packageMap.mapUid(entry.workSourceUid),
entry.threadName,
entry.handlerClassName,
entry.messageName,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 87a42fa..4678fec 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -956,18 +956,6 @@
// INetworkManagementService members
//
@Override
- public INetd getNetdService() throws RemoteException {
- final CountDownLatch connectedSignal = mConnectedSignal;
- if (connectedSignal != null) {
- try {
- connectedSignal.await();
- } catch (InterruptedException ignored) {}
- }
-
- return mNetdService;
- }
-
- @Override
public String[] listInterfaces() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java
index ccfac80..bb39ccc 100644
--- a/services/core/java/com/android/server/RuntimeService.java
+++ b/services/core/java/com/android/server/RuntimeService.java
@@ -94,7 +94,7 @@
// Add /data tz data set using the DistroVersion class (which libcore cannot use).
// This update mechanism will be removed after the time zone APEX is launched so this
// untidiness will disappear with it.
- String debugKeyPrefix = "core_library.timezone.data_";
+ String debugKeyPrefix = "core_library.timezone.source.data_";
String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile(
TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo);
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 574f54a..d09823e 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,9 +16,7 @@
package com.android.server;
-import android.annotation.MainThread;
import android.annotation.Nullable;
-import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -168,13 +166,13 @@
// called on handler thread
@GuardedBy("mBindLock")
protected void onBind() {
-
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
// called on handler thread
@GuardedBy("mBindLock")
protected void onUnbind() {
-
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
/**
@@ -205,12 +203,12 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+ bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
}
@Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+ bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
return super.onPackageChanged(packageName, uid, components);
}
}.register(mContext, UserHandle.ALL, true, mHandler);
@@ -243,20 +241,16 @@
return true;
}
- /** Returns thje name of the currently connected package or null. */
+ /** Returns the name of the currently connected package or null. */
@Nullable
public String getCurrentPackageName() {
ComponentName bestComponent = mBestComponent;
return bestComponent == null ? null : bestComponent.getPackageName();
}
- public int getCurrentPackageVersion() {
- return mBestVersion;
- }
-
/**
- * Runs the given BinderRunner if currently connected. Returns true if it was run, and false
- * otherwise. All invocations to runOnBinder are run serially.
+ * Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run
+ * serially.
*/
public final void runOnBinder(BinderRunner runner) {
synchronized (mBindLock) {
@@ -267,7 +261,7 @@
} catch (Exception e) {
// remote exceptions cannot be allowed to crash system server
Log.e(TAG, "exception while while running " + runner + " on " + service
- + " from " + mBestComponent.toShortString(), e);
+ + " from " + this, e);
}
}
}
@@ -280,14 +274,6 @@
UserHandle.USER_SYSTEM).isEmpty();
}
- /**
- * Searches and binds to the best package, or do nothing if the best package
- * is already bound, unless force rebinding is requested.
- *
- * @param forceRebind Force a rebinding to the best package if it's already
- * bound.
- */
- @WorkerThread
private void bindBestPackage(boolean forceRebind) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -365,7 +351,6 @@
}
}
- @WorkerThread
private void bind(ComponentName component, int version, int userId) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -382,7 +367,6 @@
UserHandle.of(userId));
}
- @WorkerThread
private void unbind() {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -396,7 +380,6 @@
mBestUserId = UserHandle.USER_NULL;
}
- @MainThread
@Override
public final void onServiceConnected(ComponentName component, IBinder binder) {
mHandler.post(() -> {
@@ -410,7 +393,6 @@
});
}
- @MainThread
@Override
public final void onServiceDisconnected(ComponentName component) {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e933bd0..4b092b2 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT;
@@ -99,6 +101,7 @@
import android.os.storage.VolumeRecord;
import android.provider.MediaStore;
import android.provider.Settings;
+import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -1015,7 +1018,7 @@
// On an encrypted device we can't see system properties yet, so pull
// the system locale out of the mount service.
- if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
+ if ("".equals(VoldProperties.encrypt_progress().orElse(""))) {
copyLocaleFromMountService();
}
}
@@ -2523,6 +2526,19 @@
}
}
+ /**
+ * Signal that checkpointing partitions should commit changes
+ */
+ @Override
+ public void commitChanges() throws RemoteException {
+ // Only the system process is permitted to commit checkpoints
+ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("no permission to commit checkpoint changes");
+ }
+
+ mVold.commitChanges();
+ }
+
@Override
public String getPassword() throws RemoteException {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
@@ -2757,6 +2773,14 @@
public @Nullable ParcelFileDescriptor openProxyFileDescriptor(
int mountId, int fileId, int mode) {
Slog.v(TAG, "mountProxyFileDescriptor");
+
+ // We only support a narrow set of incoming mode flags
+ if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
+ mode = MODE_READ_WRITE;
+ } else {
+ mode = MODE_READ_ONLY;
+ }
+
try {
synchronized (mAppFuseLock) {
if (mAppFuseBridge == null) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a2cbfaa..b04ae17 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -67,7 +67,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
/**
@@ -196,6 +198,8 @@
private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
+ private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
+
private int[] mSrvccState;
private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -229,8 +233,9 @@
| PhoneStateListener.LISTEN_CELL_INFO;
static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
- PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
+ PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+ | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+ | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -357,6 +362,7 @@
mCellInfo = new ArrayList<List<CellInfo>>();
mSrvccState = new int[numPhones];
mPhysicalChannelConfigs = new ArrayList<List<PhysicalChannelConfig>>();
+ mEmergencyNumberList = new HashMap<>();
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -752,6 +758,13 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
+ try {
+ r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
try {
r.callback.onPhoneCapabilityChanged(mPhoneCapability);
@@ -1665,10 +1678,30 @@
}
@Override
- public void notifyEmergencyNumberList(List<EmergencyNumber> emergencyNumberList) {
- // TODO checkPermission, modify Listener constent documentation
- // TODO implement multisim emergency number list update in listener
- // TODO implement PhoneStateListenerTest
+ public void notifyEmergencyNumberList() {
+ if (!checkNotifyPermission("notifyEmergencyNumberList()")) {
+ return;
+ }
+
+ synchronized (mRecords) {
+ mEmergencyNumberList = TelephonyManager.getDefault().getCurrentEmergencyNumberList();
+
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST)) {
+ try {
+ r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+ if (VDBG) {
+ log("notifyEmergencyNumberList: emergencyNumberList= "
+ + mEmergencyNumberList);
+ }
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
}
@@ -1710,6 +1743,7 @@
pw.println("mPhoneCapability=" + mPhoneCapability);
pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
pw.println("mRadioPowerState=" + mRadioPowerState);
+ pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index bbb1d13..9f353a8 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -16,7 +16,9 @@
package com.android.server;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -111,6 +113,7 @@
private final boolean mSupportsAmplitudeControl;
private final int mDefaultVibrationAmplitude;
private final SparseArray<VibrationEffect> mFallbackEffects;
+ private final SparseArray<Integer> mProcStatesCache = new SparseArray();
private final WorkSource mTmpWorkSource = new WorkSource();
private final Handler mH = new Handler();
private final Object mLock = new Object();
@@ -147,6 +150,25 @@
native static void vibratorSetAmplitude(int amplitude);
native static long vibratorPerformEffect(long effect, long strength);
+ private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ mProcStatesCache.put(uid, procState);
+ }
+
+ @Override public void onUidGone(int uid, boolean disabled) {
+ mProcStatesCache.delete(uid);
+ }
+
+ @Override public void onUidActive(int uid) {
+ }
+
+ @Override public void onUidIdle(int uid, boolean disabled) {
+ }
+
+ @Override public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
private class Vibration implements IBinder.DeathRecipient {
public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
@@ -411,6 +433,14 @@
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
updateVibrators();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -502,6 +532,12 @@
return;
}
verifyIncomingUid(uid);
+ if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
+ > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
+ + uid + " is background");
+ return;
+ }
if (!verifyVibrationEffect(effect)) {
return;
}
diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
new file mode 100644
index 0000000..629e882
--- /dev/null
+++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.ActivityThread;
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.util.Slog;
+
+/**
+ * Receiver responsible for updating the wallpaper when the device
+ * configuration has changed.
+ *
+ * @hide
+ */
+public class WallpaperUpdateReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "WallpaperUpdateReceiver";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ if (DEBUG) Slog.d(TAG, "onReceive: " + intent);
+
+ if (intent != null && Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(intent.getAction())) {
+ AsyncTask.execute(this::updateWallpaper);
+ }
+ }
+
+ private void updateWallpaper() {
+ try {
+ ActivityThread currentActivityThread = ActivityThread.currentActivityThread();
+ Context uiContext = currentActivityThread.getSystemUiContext();
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(uiContext);
+ if (DEBUG) Slog.d(TAG, "Set customized default_wallpaper.");
+ Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ // set a blank wallpaper to force a redraw of default_wallpaper
+ wallpaperManager.setBitmap(blank);
+ wallpaperManager.setResource(com.android.internal.R.drawable.default_wallpaper);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to customize system wallpaper." + e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index 8cd9d188..15de3de 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -133,7 +133,7 @@
proto.write(ActiveInstrumentationProto.TARGET_PROCESSES, p);
}
if (mTargetInfo != null) {
- mTargetInfo.writeToProto(proto, ActiveInstrumentationProto.TARGET_INFO);
+ mTargetInfo.writeToProto(proto, ActiveInstrumentationProto.TARGET_INFO, 0);
}
proto.write(ActiveInstrumentationProto.PROFILE_FILE, mProfileFile);
proto.write(ActiveInstrumentationProto.WATCHER, mWatcher.toString());
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a19e928..f7acf7e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1205,10 +1205,20 @@
android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
r.app.pid, r.appInfo.uid, "startForeground");
}
- } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
- mAm.enforcePermission(
- android.Manifest.permission.FOREGROUND_SERVICE,
- r.app.pid, r.appInfo.uid, "startForeground");
+ } else {
+ if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
+ mAm.enforcePermission(
+ android.Manifest.permission.FOREGROUND_SERVICE,
+ r.app.pid, r.appInfo.uid, "startForeground");
+ }
+ if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ if (r.serviceInfo.getForegroundServiceType()
+ == ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED) {
+ // STOPSHIP(b/120611119): replace log message with SecurityException.
+ Slog.w(TAG, "missing foregroundServiceType attribute in "
+ + "service element of manifest file");
+ }
+ }
}
boolean alreadyStartedOp = false;
boolean stopProcStatsOp = false;
@@ -1801,16 +1811,25 @@
for (int i = clist.size() - 1; i >= 0; i--) {
final ConnectionRecord crec = clist.get(i);
final ServiceRecord srec = crec.binding.service;
- if (srec != null && srec.app != null
- && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
- if (group > 0) {
- srec.app.connectionService = srec;
- srec.app.connectionGroup = group;
- srec.app.connectionImportance = importance;
+ if (srec != null && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ if (srec.app != null) {
+ if (group > 0) {
+ srec.app.connectionService = srec;
+ srec.app.connectionGroup = group;
+ srec.app.connectionImportance = importance;
+ } else {
+ srec.app.connectionService = null;
+ srec.app.connectionGroup = 0;
+ srec.app.connectionImportance = 0;
+ }
} else {
- srec.app.connectionService = null;
- srec.app.connectionGroup = 0;
- srec.app.connectionImportance = 0;
+ if (group > 0) {
+ srec.pendingConnectionGroup = group;
+ srec.pendingConnectionImportance = importance;
+ } else {
+ srec.pendingConnectionGroup = 0;
+ srec.pendingConnectionImportance = 0;
+ }
}
}
}
@@ -2058,8 +2077,8 @@
sInfo.applicationInfo.uid, name.getPackageName(),
name.getClassName());
}
- r = new ServiceRecord(mAm, ss, className, name, filter, sInfo, callingFromFg,
- res);
+ r = new ServiceRecord(mAm, ss, className, name, filter, sInfo,
+ callingFromFg, res);
res.setService(r);
smap.mServicesByInstanceName.put(name, r);
smap.mServicesByIntent.put(filter, r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0e354d5..69cfcc2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
import android.os.WorkSource;
import android.os.storage.StorageManager;
import android.provider.Settings;
+import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.SuggestionSpan;
@@ -666,16 +667,50 @@
final class PidMap {
private final SparseArray<ProcessRecord> mPidMap = new SparseArray<>();
+ /**
+ * Puts the process record in the map.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
void put(int key, ProcessRecord value) {
- mPidMap.put(key, value);
+ synchronized (this) {
+ mPidMap.put(key, value);
+ }
mAtmInternal.onProcessMapped(key, value.getWindowProcessController());
}
+ /**
+ * Removes the process record from the map.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
void remove(int pid) {
- mPidMap.remove(pid);
+ synchronized (this) {
+ mPidMap.remove(pid);
+ }
mAtmInternal.onProcessUnMapped(pid);
}
+ /**
+ * Removes the process record from the map if it has a thread.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
+ boolean removeIfNoThread(int pid) {
+ boolean removed = false;
+ synchronized (this) {
+ final ProcessRecord app = get(pid);
+ if (app != null && app.thread == null) {
+ mPidMap.remove(pid);
+ removed = true;
+ }
+ }
+ if (removed) {
+ mAtmInternal.onProcessUnMapped(pid);
+ }
+ return removed;
+ }
+
ProcessRecord get(int pid) {
return mPidMap.get(pid);
}
@@ -1164,8 +1199,8 @@
void setProfileProc(ProcessRecord profileProc) {
mProfileProc = profileProc;
if (mAtmInternal != null) {
- mAtmInternal.setProfileProc(
- profileProc.getWindowProcessController());
+ mAtmInternal.setProfileProc(profileProc == null ? null
+ : profileProc.getWindowProcessController());
}
}
@@ -1889,9 +1924,7 @@
app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.put(app.pid, app);
- }
+ mPidsSelfLocked.put(app.pid, app);
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
@@ -2691,47 +2724,86 @@
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
- void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+ /**
+ * Update battery stats on the activity' usage.
+ * @param activity
+ * @param uid
+ * @param userId
+ * @param resumed
+ */
+ void updateBatteryStats(ComponentName activity, int uid, int userId, boolean resumed) {
if (DEBUG_SWITCH) {
Slog.d(TAG_SWITCH,
- "updateUsageStats: comp=" + activity + "res=" + resumed);
+ "updateBatteryStats: comp=" + activity + "res=" + resumed);
}
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
- uid, activity.getPackageName(),
- activity.getShortClassName(), resumed ?
- StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
+ uid, activity.getPackageName(), activity.getShortClassName(),
+ resumed ? StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
- if (resumed) {
- if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(activity, userId,
- UsageEvents.Event.MOVE_TO_FOREGROUND);
-
- }
- synchronized (stats) {
+ synchronized (stats) {
+ if (resumed) {
stats.noteActivityResumedLocked(uid);
- }
- } else {
- if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(activity, userId,
- UsageEvents.Event.MOVE_TO_BACKGROUND);
- }
- synchronized (stats) {
+ } else {
stats.noteActivityPausedLocked(uid);
}
}
}
+ /**
+ * Update UsageStas on the activity's usage.
+ * @param activity
+ * @param userId
+ * @param event
+ * @param appToken ActivityRecord's appToken.
+ */
+ public void updateActivityUsageStats(ComponentName activity, int userId, int event,
+ IBinder appToken) {
+ if (DEBUG_SWITCH) {
+ Slog.d(TAG_SWITCH, "updateActivityUsageStats: comp="
+ + activity + " hash=" + appToken.hashCode() + " event=" + event);
+ }
+ synchronized (this) {
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode());
+ }
+ }
+ }
+
+ /**
+ * Update UsageStats on this package's usage.
+ * @param packageName
+ * @param userId
+ * @param event
+ */
+ public void updateActivityUsageStats(String packageName, int userId, int event) {
+ if (DEBUG_SWITCH) {
+ Slog.d(TAG_SWITCH, "updateActivityUsageStats: package="
+ + packageName + " event=" + event);
+ }
+ synchronized (this) {
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(packageName, userId, event);
+ }
+ }
+ }
+
+ /**
+ * Update Usages on this foreground service's usage.
+ * @param service
+ * @param userId
+ * @param started
+ */
void updateForegroundServiceUsageStats(ComponentName service, int userId, boolean started) {
if (DEBUG_SWITCH) {
Slog.d(TAG_SWITCH, "updateForegroundServiceUsageStats: comp="
- + service + "started=" + started);
+ + service + " started=" + started);
}
synchronized (this) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(service, userId,
started ? UsageEvents.Event.FOREGROUND_SERVICE_START
- : UsageEvents.Event.FOREGROUND_SERVICE_STOP);
+ : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0);
}
}
}
@@ -4254,14 +4326,7 @@
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
- boolean gone = false;
- synchronized (mPidsSelfLocked) {
- ProcessRecord knownApp = mPidsSelfLocked.get(pid);
- if (knownApp != null && knownApp.thread == null) {
- mPidsSelfLocked.remove(pid);
- gone = true;
- }
- }
+ boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
@@ -4782,8 +4847,8 @@
SystemProperties.set("sys.boot_completed", "1");
// And trigger dev.bootcomplete if we are not showing encryption progress
- if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
- || "".equals(SystemProperties.get("vold.encrypt_progress"))) {
+ if (!"trigger_restart_min_framework".equals(VoldProperties.decrypt().orElse(""))
+ || "".equals(VoldProperties.encrypt_progress().orElse(""))) {
SystemProperties.set("dev.bootcomplete", "1");
}
mUserController.sendBootCompleted(
@@ -7582,7 +7647,7 @@
String extraOptions = null;
switch (bugreportType) {
case ActivityManager.BUGREPORT_OPTION_FULL:
- // Default options.
+ extraOptions = "bugreportfull";
break;
case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
extraOptions = "bugreportplus";
@@ -9183,6 +9248,19 @@
}
sdumper.dumpWithClient();
}
+ if (dumpPackage == null) {
+ // Intentionally dropping the lock for this, because dumpBinderProxies() will make many
+ // outgoing binder calls to retrieve interface descriptors; while that is system code,
+ // there is nothing preventing an app from overriding this implementation by talking to
+ // the binder driver directly, and hang up system_server in the process. So, dump
+ // without locks held, and even then only when there is an unreasonably large number of
+ // proxies in the first place.
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */);
+ }
synchronized(this) {
pw.println();
if (dumpAll) {
@@ -9248,19 +9326,6 @@
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
}
- if (dumpPackage == null) {
- // Intentionally dropping the lock for this, because dumpBinderProxies() will make many
- // outgoing binder calls to retrieve interface descriptors; while that is system code,
- // there is nothing preventing an app from overriding this implementation by talking to
- // the binder driver directly, and hang up system_server in the process. So, dump
- // without locks held, and even then only when there is an unreasonably large number of
- // proxies in the first place.
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
- dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */);
- }
}
/**
@@ -10240,7 +10305,8 @@
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS, mProcessList.mLruProcesses.indexOf(r)
+ );
if (r.isPersistent()) {
numPers++;
}
@@ -10252,7 +10318,9 @@
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS);
+ r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS,
+ mProcessList.mLruProcesses.indexOf(r)
+ );
}
for (int i=0; i<mActiveInstrumentation.size(); i++) {
@@ -10350,7 +10418,7 @@
writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
- mAtmInternal.writeProcessesToProto(proto, dumpPackage);
+ mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, mTestPssMode);
if (dumpPackage == null) {
mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
@@ -10380,14 +10448,6 @@
}
}
- if (dumpPackage == null) {
- final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
- proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
- PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
- proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
- proto.end(sleepToken);
- }
-
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
if (dumpPackage == null || dumpPackage.equals(mDebugApp)
@@ -13113,11 +13173,8 @@
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
- boolean removed;
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.remove(app.pid);
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
+ mPidsSelfLocked.remove(app.pid);
+ mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
@@ -17514,8 +17571,9 @@
// how many slots we have for background processes; we may want
// to put multiple processes in a slot of there are enough of
// them.
- int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
- - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
+ final int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
+ - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2
+ / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
if (numEmptyProcs > cachedProcessLimit) {
// If there are more empty processes than our limit on cached
@@ -17526,17 +17584,19 @@
// instead of a gazillion empty processes.
numEmptyProcs = cachedProcessLimit;
}
- int emptyFactor = numEmptyProcs/numSlots;
+ int emptyFactor = (numEmptyProcs + numSlots - 1) / numSlots;
if (emptyFactor < 1) emptyFactor = 1;
- int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;
+ int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + numSlots - 1) : 1)
+ / numSlots;
if (cachedFactor < 1) cachedFactor = 1;
- int stepCached = 0;
- int stepEmpty = 0;
+ int stepCached = -1;
+ int stepEmpty = -1;
int numCached = 0;
int numCachedExtraGroup = 0;
int numEmpty = 0;
int numTrimming = 0;
int lastCachedGroup = 0;
+ int lastCachedGroupImportance = 0;
int lastCachedGroupUid = 0;
mNumNonCachedProcs = 0;
@@ -17545,9 +17605,10 @@
// First update the OOM adjustment for each of the
// application processes based on their current state.
int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
- int nextCachedAdj = curCachedAdj+1;
- int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
- int nextEmptyAdj = curEmptyAdj+2;
+ int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+ int curCachedImpAdj = 0;
+ int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
boolean retryCycles = false;
@@ -17572,27 +17633,61 @@
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
- // This process is a cached process holding activities...
- // assign it the next cached value for that type, and then
- // step that cached level.
- app.setCurRawAdj(curCachedAdj);
- app.curAdj = app.modifyRawOomAdj(curCachedAdj);
- if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
- + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
- + ")");
- if (curCachedAdj != nextCachedAdj) {
+ // Figure out the next cached level, taking into account groups.
+ boolean inGroup = false;
+ if (app.connectionGroup != 0) {
+ if (lastCachedGroupUid == app.uid
+ && lastCachedGroup == app.connectionGroup) {
+ // This is in the same group as the last process, just tweak
+ // adjustment by importance.
+ if (app.connectionImportance > lastCachedGroupImportance) {
+ lastCachedGroupImportance = app.connectionImportance;
+ if (curCachedAdj < nextCachedAdj
+ && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
+ curCachedImpAdj++;
+ }
+ }
+ inGroup = true;
+ } else {
+ lastCachedGroupUid = app.uid;
+ lastCachedGroup = app.connectionGroup;
+ lastCachedGroupImportance = app.connectionImportance;
+ }
+ }
+ if (!inGroup && curCachedAdj != nextCachedAdj) {
stepCached++;
+ curCachedImpAdj = 0;
if (stepCached >= cachedFactor) {
stepCached = 0;
curCachedAdj = nextCachedAdj;
- nextCachedAdj += 2;
+ nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
+ // This process is a cached process holding activities...
+ // assign it the next cached value for that type, and then
+ // step that cached level.
+ app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+ app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
+ if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+ + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+ + " curCachedImpAdj=" + curCachedImpAdj + ")");
break;
default:
+ // Figure out the next cached level.
+ if (curEmptyAdj != nextEmptyAdj) {
+ stepEmpty++;
+ if (stepEmpty >= emptyFactor) {
+ stepEmpty = 0;
+ curEmptyAdj = nextEmptyAdj;
+ nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+ if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+ }
+ }
+ }
// For everything else, assign next empty cached process
// level and bump that up. Note that this means that
// long-running services that have dropped down to the
@@ -17603,22 +17698,9 @@
if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+ " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+ ")");
- if (curEmptyAdj != nextEmptyAdj) {
- stepEmpty++;
- if (stepEmpty >= emptyFactor) {
- stepEmpty = 0;
- curEmptyAdj = nextEmptyAdj;
- nextEmptyAdj += 2;
- if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
- nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
- }
- }
- }
break;
}
}
-
-
}
}
@@ -17650,6 +17732,8 @@
}
}
+ lastCachedGroup = lastCachedGroupUid = 0;
+
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
@@ -18917,7 +19001,7 @@
ProcessMemoryState processMemoryState =
new ProcessMemoryState(uid,
r.processName,
- r.maxAdj,
+ r.curAdj,
memoryStat.pgfault,
memoryStat.pgmajfault,
memoryStat.rssInBytes,
@@ -19023,9 +19107,19 @@
}
@Override
- public void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+ public void updateBatteryStats(ComponentName activity, int uid, int userId,
+ boolean resumed) {
synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.updateUsageStats(activity, uid, userId, resumed);
+ ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed);
+ }
+ }
+
+ @Override
+ public void updateActivityUsageStats(ComponentName activity, int userId, int event,
+ IBinder appToken) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.updateActivityUsageStats(activity, userId, event,
+ appToken);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8c39d75..3a0899d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1337,7 +1337,7 @@
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
+ r.curComponent.getPackageName(), false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 9cfd39c..65cd329 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -55,7 +55,12 @@
// add other system settings here...
sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class);
- sGlobalSettingToTypeMap.put(Settings.Global.ANGLE_ENABLED_APP, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 7991783..117984e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -137,9 +137,13 @@
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
- static final int CACHED_APP_MAX_ADJ = 906;
+ static final int CACHED_APP_MAX_ADJ = 999;
static final int CACHED_APP_MIN_ADJ = 900;
+ // Number of levels we have available for different service connection group importance
+ // levels.
+ static final int CACHED_APP_IMPORTANCE_LEVELS = 5;
+
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
static final int SERVICE_B_ADJ = 800;
@@ -537,40 +541,44 @@
}
private static String buildOomTag(String prefix, String space, int val, int base) {
- if (val == base) {
+ final int diff = val - base;
+ if (diff == 0) {
if (space == null) return prefix;
- return prefix + " ";
+ return prefix + space;
}
- return prefix + "+" + Integer.toString(val - base);
+ if (diff < 10) {
+ return prefix + "+ " + Integer.toString(diff);
+ }
+ return prefix + "+" + Integer.toString(diff);
}
public static String makeOomAdjString(int setAdj) {
if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- return buildOomTag("cch", " ", setAdj, ProcessList.CACHED_APP_MIN_ADJ);
+ return buildOomTag("cch", " ", setAdj, ProcessList.CACHED_APP_MIN_ADJ);
} else if (setAdj >= ProcessList.SERVICE_B_ADJ) {
- return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ);
+ return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ);
} else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
- return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ);
+ return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ);
} else if (setAdj >= ProcessList.HOME_APP_ADJ) {
- return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ);
+ return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ);
} else if (setAdj >= ProcessList.SERVICE_ADJ) {
- return buildOomTag("svc ", null, setAdj, ProcessList.SERVICE_ADJ);
+ return buildOomTag("svc ", null, setAdj, ProcessList.SERVICE_ADJ);
} else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
- return buildOomTag("hvy ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ);
+ return buildOomTag("hvy ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ);
} else if (setAdj >= ProcessList.BACKUP_APP_ADJ) {
- return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ);
+ return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ);
} else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
- return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ);
+ return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ);
} else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) {
- return buildOomTag("vis ", null, setAdj, ProcessList.VISIBLE_APP_ADJ);
+ return buildOomTag("vis", " ", setAdj, ProcessList.VISIBLE_APP_ADJ);
} else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) {
- return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ);
+ return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ);
} else if (setAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
- return buildOomTag("psvc ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ);
+ return buildOomTag("psvc ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ);
} else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) {
- return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ);
+ return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ);
} else if (setAdj >= ProcessList.SYSTEM_ADJ) {
- return buildOomTag("sys ", null, setAdj, ProcessList.SYSTEM_ADJ);
+ return buildOomTag("sys ", null, setAdj, ProcessList.SYSTEM_ADJ);
} else if (setAdj >= ProcessList.NATIVE_ADJ) {
return buildOomTag("ntv ", null, setAdj, ProcessList.NATIVE_ADJ);
} else {
@@ -1246,10 +1254,8 @@
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
- synchronized (mService.mPidsSelfLocked) {
- mService.mPidsSelfLocked.remove(app.pid);
- mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
+ mService.mPidsSelfLocked.remove(app.pid);
+ mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startTime, "startProcess: done removing from pids map");
app.setPid(0);
}
@@ -1767,8 +1773,8 @@
mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
true /*replacingPid*/);
}
+ mService.mPidsSelfLocked.put(pid, app);
synchronized (mService.mPidsSelfLocked) {
- mService.mPidsSelfLocked.put(pid, app);
if (!procAttached) {
Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
@@ -1928,10 +1934,8 @@
.pendingStart)) {
int pid = app.pid;
if (pid > 0) {
- synchronized (mService.mPidsSelfLocked) {
- mService.mPidsSelfLocked.remove(pid);
- mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
+ mService.mPidsSelfLocked.remove(pid);
+ mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
@@ -2241,6 +2245,191 @@
return index;
}
+ /**
+ * Handle the case where we are inserting a process hosting client activities:
+ * Make sure any groups have their order match their importance, and take care of
+ * distributing old clients across other activity processes so they can't spam
+ * the LRU list. Processing of the list will be restricted by the indices provided,
+ * and not extend out of them.
+ *
+ * @param topApp The app at the top that has just been inserted in to the list.
+ * @param topI The position in the list where topApp was inserted; this is the start (at the
+ * top) where we are going to do our processing.
+ * @param bottomI The last position at which we will be processing; this is the end position
+ * of whichever section of the LRU list we are in. Nothing past it will be
+ * touched.
+ * @param endIndex The current end of the top being processed. Typically topI - 1. That is,
+ * where we are going to start potentially adjusting other entries in the list.
+ */
+ private void updateClientActivitiesOrdering(final ProcessRecord topApp, final int topI,
+ final int bottomI, int endIndex) {
+ if (topApp.hasActivitiesOrRecentTasks() || topApp.treatLikeActivity
+ || !topApp.hasClientActivities()) {
+ // If this is not a special process that has client activities, then there is
+ // nothing to do.
+ return;
+ }
+
+ final int uid = topApp.info.uid;
+ if (topApp.connectionGroup > 0) {
+ int endImportance = topApp.connectionImportance;
+ for (int i = endIndex; i >= bottomI; i--) {
+ final ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid == uid
+ && subProc.connectionGroup == topApp.connectionGroup) {
+ if (i == endIndex && subProc.connectionImportance >= endImportance) {
+ // This process is already in the group, and its importance
+ // is not as strong as the process before it, so keep it
+ // correctly positioned in the group.
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Keeping in-place above " + subProc
+ + " endImportance=" + endImportance
+ + " group=" + subProc.connectionGroup
+ + " importance=" + subProc.connectionImportance);
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ } else {
+ // We want to pull this up to be with the rest of the group,
+ // and order within the group by importance.
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Pulling up " + subProc
+ + " to position in group with importance="
+ + subProc.connectionImportance);
+ boolean moved = false;
+ for (int pos = topI; pos > endIndex; pos--) {
+ final ProcessRecord posProc = mLruProcesses.get(pos);
+ if (subProc.connectionImportance
+ <= posProc.connectionImportance) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(pos, subProc);
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Moving " + subProc
+ + " from position " + i + " to above " + posProc
+ + " @ " + pos);
+ moved = true;
+ endIndex--;
+ break;
+ }
+ }
+ if (!moved) {
+ // Goes to the end of the group.
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex - 1, subProc);
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Moving " + subProc
+ + " from position " + i + " to end of group @ "
+ + endIndex);
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ }
+ }
+ }
+ }
+
+ }
+ // To keep it from spamming the LRU list (by making a bunch of clients),
+ // we will distribute other entries owned by it to be in-between other apps.
+ int i = endIndex;
+ while (i >= bottomI) {
+ ProcessRecord subProc = mLruProcesses.get(i);
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Looking to spread old procs, at " + subProc + " @ " + i);
+ if (subProc.info.uid != uid) {
+ // This is a different app... if we have gone through some of the
+ // target app, pull this up to be before them. We want to pull up
+ // one activity process, but any number of non-activity processes.
+ if (i < endIndex) {
+ boolean hasActivity = false;
+ int connUid = 0;
+ int connGroup = 0;
+ while (i >= bottomI) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex, subProc);
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Different app, moving to " + endIndex);
+ i--;
+ if (i < bottomI) {
+ break;
+ }
+ subProc = mLruProcesses.get(i);
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Looking at next app at " + i + ": " + subProc);
+ if (subProc.hasActivitiesOrRecentTasks() || subProc.treatLikeActivity) {
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "This is hosting an activity!");
+ if (hasActivity) {
+ // Already found an activity, done.
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Already found an activity, done");
+ break;
+ }
+ hasActivity = true;
+ } else if (subProc.hasClientActivities()) {
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "This is a client of an activity");
+ if (hasActivity) {
+ if (connUid == 0 || connUid != subProc.info.uid) {
+ // Already have an activity that is not from from a client
+ // connection or is a different client connection, done.
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Already found a different activity: connUid="
+ + connUid + " uid=" + subProc.info.uid);
+ break;
+ } else if (connGroup == 0 || connGroup != subProc.connectionGroup) {
+ // Previously saw a different group or not from a group,
+ // want to treat these as different things.
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Already found a different group: connGroup="
+ + connGroup + " group=" + subProc.connectionGroup);
+ break;
+ }
+ } else {
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "This is an activity client! uid="
+ + subProc.info.uid + " group=" + subProc.connectionGroup);
+ hasActivity = true;
+ connUid = subProc.info.uid;
+ connGroup = subProc.connectionGroup;
+ }
+ }
+ endIndex--;
+ }
+ }
+ // Find the end of the next group of processes for target app. This
+ // is after any entries of different apps (so we don't change the existing
+ // relative order of apps) and then after the next last group of processes
+ // of the target app.
+ for (endIndex--; endIndex >= bottomI; endIndex--) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ if (endProc.info.uid == uid) {
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Found next group of app: " + endProc + " @ "
+ + endIndex);
+ break;
+ }
+ }
+ if (endIndex >= bottomI) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ for (endIndex--; endIndex >= bottomI; endIndex--) {
+ final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+ if (nextEndProc.info.uid != uid
+ || nextEndProc.connectionGroup != endProc.connectionGroup) {
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Found next group or app: " + nextEndProc + " @ "
+ + endIndex + " group=" + nextEndProc.connectionGroup);
+ break;
+ }
+ }
+ }
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Bumping scan position to " + endIndex);
+ i = endIndex;
+ } else {
+ i--;
+ }
+ }
+ }
+
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client) {
final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
@@ -2353,91 +2542,31 @@
if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
- // activities... move it up, but one below the top (the top
- // should always have a real activity).
+ // activities... move it up, but below the app that is binding to it.
if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Adding to second-top of LRU activity list: " + app);
- mLruProcesses.add(N - 1, app);
+ "Adding to second-top of LRU activity list: " + app
+ + " group=" + app.connectionGroup
+ + " importance=" + app.connectionImportance);
+ int pos = N - 1;
+ while (pos > mLruProcessActivityStart) {
+ final ProcessRecord posproc = mLruProcesses.get(pos);
+ if (posproc.info.uid == app.info.uid) {
+ // Technically this app could have multiple processes with different
+ // activities and so we should be looking for the actual process that
+ // is bound to the target proc... but I don't really care, do you?
+ break;
+ }
+ pos--;
+ }
+ mLruProcesses.add(pos, app);
// If this process is part of a group, need to pull up any other processes
// in that group to be with it.
- final int uid = app.info.uid;
- int endIndex = N - 2;
- nextActivityIndex = N - 2;
- if (app.connectionGroup > 0) {
- int endImportance = app.connectionImportance;
- for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
- final ProcessRecord subProc = mLruProcesses.get(i);
- if (subProc.info.uid == uid
- && subProc.connectionGroup == subProc.connectionGroup) {
- if (i == endIndex && subProc.connectionImportance >= endImportance) {
- // This process is already in the group, and its importance
- // is not as strong as the process before it, so it keep it
- // correctly positioned in the group.
- endIndex--;
- endImportance = subProc.connectionImportance;
- } else {
- // We want to pull this up to be with the rest of the group,
- // and order within the group by importance.
- boolean moved = false;
- for (int pos = N - 1; pos > endIndex; pos--) {
- final ProcessRecord posProc = mLruProcesses.get(pos);
- if (subProc.connectionImportance
- <= posProc.connectionImportance) {
- mLruProcesses.remove(i);
- mLruProcesses.add(pos, subProc);
- moved = true;
- endIndex--;
- break;
- }
- }
- if (!moved) {
- // Goes to the end of the group.
- mLruProcesses.remove(i);
- mLruProcesses.add(endIndex - 1, subProc);
- endIndex--;
- endImportance = subProc.connectionImportance;
- }
- }
- }
- }
-
+ int endIndex = pos - 1;
+ if (endIndex < mLruProcessActivityStart) {
+ endIndex = mLruProcessActivityStart;
}
- // To keep it from spamming the LRU list (by making a bunch of clients),
- // we will distribute other entries owned by it to be in-between other apps.
- for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
- final ProcessRecord subProc = mLruProcesses.get(i);
- if (subProc.info.uid != uid) {
- // This is a different app... if we have gone through some of the
- // target app, pull this up to be before them.
- if (i < endIndex) {
- mLruProcesses.remove(i);
- mLruProcesses.add(endIndex, subProc);
- }
- // Find the end of the next group of processes for target app. This
- // is after any entries of different apps (so we don't change the existing
- // relative order of apps) and then after the next last group of processes
- // of the target app.
- for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
- final ProcessRecord endProc = mLruProcesses.get(endIndex);
- if (endProc.info.uid == uid) {
- break;
- }
- }
- if (endIndex >= mLruProcessActivityStart) {
- final ProcessRecord endProc = mLruProcesses.get(endIndex);
- for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
- final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
- if (nextEndProc.info.uid != uid
- || nextEndProc.connectionGroup != endProc.connectionGroup) {
- break;
- }
- }
- }
- if (i > endIndex) {
- i = endIndex;
- }
- }
- }
+ nextActivityIndex = endIndex;
+ updateClientActivitiesOrdering(app, pos, mLruProcessActivityStart, endIndex);
} else {
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
@@ -2473,6 +2602,9 @@
nextIndex = index - 1;
mLruProcessActivityStart++;
mLruProcessServiceStart++;
+ if (index > 1) {
+ updateClientActivitiesOrdering(app, mLruProcessServiceStart - 1, 0, index - 1);
+ }
}
app.lruSeq = mLruSeq;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 013de93..4826f48 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -586,7 +586,7 @@
}
origBase.makeInactive();
}
- baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
+ baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
info.longVersionCode, processName);
baseProcessTracker.makeActive();
for (int i=0; i<pkgList.size(); i++) {
@@ -594,7 +594,7 @@
if (holder.state != null && holder.state != origBase) {
holder.state.makeInactive();
}
- tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), uid,
+ tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
info.longVersionCode, processName);
if (holder.state != baseProcessTracker) {
holder.state.makeActive();
@@ -760,19 +760,25 @@
@Override
public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ writeToProto(proto, fieldId, -1);
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId, int lruIndex) {
long token = proto.start(fieldId);
proto.write(ProcessRecordProto.PID, pid);
proto.write(ProcessRecordProto.PROCESS_NAME, processName);
- if (info.uid < Process.FIRST_APPLICATION_UID) {
- proto.write(ProcessRecordProto.UID, uid);
- } else {
+ proto.write(ProcessRecordProto.UID, info.uid);
+ if (UserHandle.getAppId(info.uid) >= Process.FIRST_APPLICATION_UID) {
proto.write(ProcessRecordProto.USER_ID, userId);
proto.write(ProcessRecordProto.APP_ID, UserHandle.getAppId(info.uid));
- if (uid != info.uid) {
- proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
- }
+ }
+ if (uid != info.uid) {
+ proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
}
proto.write(ProcessRecordProto.PERSISTENT, mPersistent);
+ if (lruIndex >= 0) {
+ proto.write(ProcessRecordProto.LRU_INDEX, lruIndex);
+ }
proto.end(token);
}
@@ -864,7 +870,8 @@
ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
versionCode);
if (baseProcessTracker != null) {
- tracker.updateProcessStateHolderLocked(holder, pkg, uid, versionCode, processName);
+ tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
+ processName);
pkgList.put(pkg, holder);
if (holder.state != baseProcessTracker) {
holder.state.makeActive();
@@ -925,7 +932,7 @@
pkgList.clear();
ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
info.longVersionCode);
- tracker.updateProcessStateHolderLocked(holder, info.packageName, uid,
+ tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
info.longVersionCode, processName);
pkgList.put(info.packageName, holder);
if (holder.state != baseProcessTracker) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 09f8c3e..da5ce1c 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -121,6 +121,8 @@
long nextRestartTime; // time when restartDelay will expire.
boolean destroying; // set when we have started destroying the service
long destroyTime; // time at which destory was initiated.
+ int pendingConnectionGroup; // To be filled in to ProcessRecord once it connects
+ int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects
String stringName; // caching of toString
@@ -386,6 +388,11 @@
pw.print(" restartTime=");
TimeUtils.formatDuration(restartTime, now, pw);
pw.print(" createdFromFg="); pw.println(createdFromFg);
+ if (pendingConnectionGroup != 0) {
+ pw.print(prefix); pw.print(" pendingConnectionGroup=");
+ pw.print(pendingConnectionGroup);
+ pw.print(" Importance="); pw.println(pendingConnectionImportance);
+ }
if (startRequested || delayedStop || lastStartId != 0) {
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
pw.print(" delayedStop="); pw.print(delayedStop);
@@ -461,7 +468,11 @@
serviceInfo = sInfo;
appInfo = sInfo.applicationInfo;
packageName = sInfo.applicationInfo.packageName;
- processName = sInfo.processName;
+ if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ processName = sInfo.processName + ":" + instanceName.getClassName();
+ } else {
+ processName = sInfo.processName;
+ }
permission = sInfo.permission;
exported = sInfo.exported;
this.restarter = restarter;
@@ -507,6 +518,12 @@
public void setProcess(ProcessRecord _proc) {
app = _proc;
+ if (pendingConnectionGroup > 0) {
+ app.connectionService = this;
+ app.connectionGroup = pendingConnectionGroup;
+ app.connectionImportance = pendingConnectionImportance;
+ pendingConnectionGroup = pendingConnectionImportance = 0;
+ }
if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 353f787..bcce052 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -67,6 +67,7 @@
import android.os.IUserManager;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -332,6 +333,14 @@
return;
}
}
+ // Inform checkpointing systems of success
+ try {
+ getStorageManager().commitChanges();
+ } catch (Exception e) {
+ PowerManager pm = (PowerManager)
+ mInjector.getContext().getSystemService(Context.POWER_SERVICE);
+ pm.reboot("Checkpoint commit failed");
+ }
// We always walk through all the user lifecycle states to send
// consistent developer events. We step into RUNNING_LOCKED here,
@@ -1144,7 +1153,7 @@
/**
* Attempt to unlock user without a credential token. This typically
* succeeds when the device doesn't have credential-encrypted storage, or
- * when the the credential-encrypted storage isn't tied to a user-provided
+ * when the credential-encrypted storage isn't tied to a user-provided
* PIN or pattern.
*/
private boolean maybeUnlockUser(final int userId) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index add55ea..1882be2 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -813,6 +813,24 @@
}
}
+ @Override // Binder call
+ public void resetTimeout(byte[] token) {
+ checkInternalPermission();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mFingerprintService != null) {
+ mFingerprintService.resetTimeout(token);
+ }
+ if (mFaceService != null) {
+ mFaceService.resetTimeout(token);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 9649ccd..32219aa 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -282,9 +282,11 @@
public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int userId, int groupId,
- byte[] cryptoToken, boolean restricted, String owner) {
+ byte[] cryptoToken, boolean restricted, String owner,
+ final int[] disabledFeatures) {
super(context, getMetrics(), daemon, halDeviceId, token, listener,
- userId, groupId, cryptoToken, restricted, owner, getBiometricUtils());
+ userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
+ disabledFeatures);
}
@Override
@@ -408,7 +410,8 @@
int cancel() throws RemoteException;
int remove(int groupId, int biometricId) throws RemoteException;
int enumerate() throws RemoteException;
- int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
+ int enroll(byte[] cryptoToken, int groupId, int timeout,
+ ArrayList<Integer> disabledFeatures) throws RemoteException;
}
/**
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index f858ef5..8a0f085 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -34,15 +34,18 @@
private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
private final byte[] mCryptoToken;
private final BiometricUtils mBiometricUtils;
+ private final int[] mDisabledFeatures;
public EnrollClient(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
- byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) {
+ byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils,
+ final int[] disabledFeatures) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
owner, 0 /* cookie */);
mBiometricUtils = utils;
mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+ mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
}
@Override
@@ -74,7 +77,13 @@
public int start() {
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
- final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout);
+ final ArrayList<Integer> disabledFeatures = new ArrayList<>();
+ for (int i = 0; i < mDisabledFeatures.length; i++) {
+ disabledFeatures.add(mDisabledFeatures[i]);
+ }
+
+ final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout,
+ disabledFeatures);
if (result != 0) {
Slog.w(getLogTag(), "startEnroll failed, result=" + result);
mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result);
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 557af04..72f73f6 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -113,17 +113,17 @@
}
@Override // Binder call
- public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
- final IFaceServiceReceiver receiver, final int flags,
- final String opPackageName) {
+ public void enroll(final IBinder token, final byte[] cryptoToken,
+ final IFaceServiceReceiver receiver, final String opPackageName,
+ final int[] disabledFeatures) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = isRestricted();
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
- 0 /* groupId */, cryptoToken, restricted, opPackageName);
+ 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures);
- enrollInternal(client, userId);
+ enrollInternal(client, UserHandle.getCallingUserId());
}
@Override // Binder call
@@ -333,7 +333,7 @@
}
@Override
- public int setRequireAttention(boolean requireAttention, final byte[] token) {
+ public int setFeature(int feature, boolean enabled, final byte[] token) {
checkPermission(MANAGE_BIOMETRIC);
final ArrayList<Byte> byteToken = new ArrayList<>();
@@ -343,10 +343,11 @@
int result;
try {
- result = mDaemon != null ? mDaemon.setRequireAttention(requireAttention, byteToken)
+ result = mDaemon != null ? mDaemon.setFeature(feature, enabled, byteToken)
: Status.INTERNAL_ERROR;
} catch (RemoteException e) {
- Slog.e(getTag(), "Unable to setRequireAttention to " + requireAttention, e);
+ Slog.e(getTag(), "Unable to set feature: " + feature + " to enabled:" + enabled,
+ e);
result = Status.INTERNAL_ERROR;
}
@@ -354,17 +355,12 @@
}
@Override
- public boolean getRequireAttention(final byte[] token) {
+ public boolean getFeature(int feature) {
checkPermission(MANAGE_BIOMETRIC);
- final ArrayList<Byte> byteToken = new ArrayList<>();
- for (int i = 0; i < token.length; i++) {
- byteToken.add(token[i]);
- }
-
boolean result = true;
try {
- result = mDaemon != null ? mDaemon.getRequireAttention(byteToken).value : true;
+ result = mDaemon != null ? mDaemon.getFeature(feature) : true;
} catch (RemoteException e) {
Slog.e(getTag(), "Unable to getRequireAttention", e);
}
@@ -612,7 +608,8 @@
}
@Override
- public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+ public int enroll(byte[] cryptoToken, int groupId, int timeout,
+ ArrayList<Integer> disabledFeatures) throws RemoteException {
IBiometricsFace daemon = getFaceDaemon();
if (daemon == null) {
Slog.w(TAG, "enroll(): no face HAL!");
@@ -623,7 +620,7 @@
token.add(cryptoToken[i]);
}
// TODO: plumb requireAttention down from framework
- return daemon.enroll(token, timeout, true /* requireAttention */);
+ return daemon.enroll(token, timeout, disabledFeatures);
}
};
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 6a5bc61..3895ef7 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -144,7 +144,7 @@
final int groupId = userId; // default group for fingerprint enrollment
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
- cryptoToken, restricted, opPackageName);
+ cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */);
enrollInternal(client, userId);
}
@@ -716,7 +716,8 @@
}
@Override
- public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+ public int enroll(byte[] cryptoToken, int groupId, int timeout,
+ ArrayList<Integer> disabledFeatures) throws RemoteException {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "enroll(): no fingerprint HAL!");
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 65537ad..258c325 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -55,7 +55,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.intelligence.IntelligenceManagerInternal;
+import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -158,7 +158,7 @@
private final IUserManager mUm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
- private final IntelligenceManagerInternal mIm;
+ private final ContentCaptureManagerInternal mContentCaptureInternal;
private final IBinder mPermissionOwner;
private HostClipboardMonitor mHostClipboardMonitor = null;
private Thread mHostMonitorThread = null;
@@ -178,7 +178,7 @@
mPm = getContext().getPackageManager();
mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
- mIm = LocalServices.getService(IntelligenceManagerInternal.class);
+ mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
@@ -652,9 +652,10 @@
case AppOpsManager.OP_READ_CLIPBOARD:
// Clipboard can only be read by applications with focus..
boolean allowed = mWm.isUidFocused(callingUid);
- if (!allowed && mIm != null) {
+ if (!allowed && mContentCaptureInternal != null) {
// ...or the Intelligence Service
- allowed = mIm.isIntelligenceServiceForUser(callingUid, userId);
+ allowed = mContentCaptureInternal.isContentCaptureServiceForUser(callingUid,
+ userId);
}
if (!allowed) {
Slog.e(TAG, "Denying clipboard access to " + callingPackage
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3c14393..d75601b 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -944,10 +944,11 @@
public boolean hasTetherableConfiguration() {
final TetheringConfiguration cfg = mConfig;
final boolean hasDownstreamConfiguration =
- (cfg.tetherableUsbRegexs.length != 0) ||
- (cfg.tetherableWifiRegexs.length != 0) ||
- (cfg.tetherableBluetoothRegexs.length != 0);
- final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty();
+ (cfg.tetherableUsbRegexs.length != 0)
+ || (cfg.tetherableWifiRegexs.length != 0)
+ || (cfg.tetherableBluetoothRegexs.length != 0);
+ final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty()
+ || cfg.chooseUpstreamAutomatically;
return hasDownstreamConfiguration && hasUpstreamConfiguration;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index b7ed2f9..602aedb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -206,45 +206,6 @@
// Handle of the user initiating VPN.
private final int mUserHandle;
- // Listen to package removal and change events (update/uninstall) for this user
- private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final Uri data = intent.getData();
- final String packageName = data == null ? null : data.getSchemeSpecificPart();
- if (packageName == null) {
- return;
- }
-
- synchronized (Vpn.this) {
- // Avoid race where always-on package has been unset
- if (!packageName.equals(getAlwaysOnPackage())) {
- return;
- }
-
- final String action = intent.getAction();
- Log.i(TAG, "Received broadcast " + action + " for always-on VPN package "
- + packageName + " in user " + mUserHandle);
-
- switch(action) {
- case Intent.ACTION_PACKAGE_REPLACED:
- // Start vpn after app upgrade
- startAlwaysOnVpn();
- break;
- case Intent.ACTION_PACKAGE_REMOVED:
- final boolean isPackageRemoved = !intent.getBooleanExtra(
- Intent.EXTRA_REPLACING, false);
- if (isPackageRemoved) {
- setAlwaysOnPackage(null, false);
- }
- break;
- }
- }
- }
- };
-
- private boolean mIsPackageIntentReceiverRegistered = false;
-
public Vpn(Looper looper, Context context, INetworkManagementService netService,
@UserIdInt int userHandle) {
this(looper, context, netService, userHandle, new SystemServices(context));
@@ -500,7 +461,6 @@
// Prepare this app. The notification will update as a side-effect of updateState().
prepareInternal(packageName);
}
- maybeRegisterPackageChangeReceiverLocked(packageName);
setVpnForcedLocked(mLockdown);
return true;
}
@@ -509,31 +469,6 @@
return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
}
- private void unregisterPackageChangeReceiverLocked() {
- if (mIsPackageIntentReceiverRegistered) {
- mContext.unregisterReceiver(mPackageIntentReceiver);
- mIsPackageIntentReceiverRegistered = false;
- }
- }
-
- private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
- // Unregister IntentFilter listening for previous always-on package change
- unregisterPackageChangeReceiverLocked();
-
- if (!isNullOrLegacyVpn(packageName)) {
- mIsPackageIntentReceiverRegistered = true;
-
- IntentFilter intentFilter = new IntentFilter();
- // Protected intent can only be sent by system. No permission required in register.
- intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
- mContext.registerReceiverAsUser(
- mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
- }
- }
-
/**
* @return the package name of the VPN controller responsible for always-on VPN,
* or {@code null} if none is set or always-on VPN is controlled through
@@ -1302,7 +1237,6 @@
setLockdown(false);
mAlwaysOn = false;
- unregisterPackageChangeReceiverLocked();
// Quit any active connections
agentDisconnect();
}
diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
new file mode 100644
index 0000000..726362a
--- /dev/null
+++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * ContentCapture Manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class ContentCaptureManagerInternal {
+
+ /**
+ * Checks whether the given {@code uid} owns the
+ * {@link android.service.contentcapture.ContentCaptureService} implementation associated with
+ * the given {@code userId}.
+ */
+ public abstract boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId);
+
+ /**
+ * Notifies the intelligence service of new assist data for the given activity.
+ *
+ * @return {@code false} if there was no service set for the given user
+ */
+ public abstract boolean sendActivityAssistData(@UserIdInt int userId,
+ @NonNull IBinder activityToken, @NonNull Bundle data);
+}
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 0b6786c..521fa23 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TypeEvaluator;
@@ -29,8 +31,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.hardware.display.IColorDisplayManager;
import android.net.Uri;
import android.opengl.Matrix;
+import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -39,6 +43,7 @@
import android.util.Slog;
import android.view.animation.AnimationUtils;
+import com.android.internal.R;
import com.android.internal.app.ColorDisplayController;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
@@ -49,12 +54,8 @@
import java.time.LocalTime;
import java.time.ZoneId;
-import com.android.internal.R;
-
-import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
-
/**
- * Tints the display at night.
+ * Controls the display's color transforms.
*/
public final class ColorDisplayService extends SystemService
implements ColorDisplayController.Callback {
@@ -101,7 +102,7 @@
@Override
public void onStart() {
- // Nothing to publish.
+ publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
}
@Override
@@ -171,7 +172,7 @@
}
};
cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
- false /* notifyForDescendents */, mUserSetupObserver, mCurrentUser);
+ false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
} else if (mBootCompleted) {
setUp();
}
@@ -405,8 +406,8 @@
}
/**
- * Returns the first date time corresponding to the local time that occurs before the
- * provided date time.
+ * Returns the first date time corresponding to the local time that occurs before the provided
+ * date time.
*
* @param compareTime the LocalDateTime to compare against
* @return the prior LocalDateTime corresponding to this local time
@@ -420,8 +421,8 @@
}
/**
- * Returns the first date time corresponding to this local time that occurs after the
- * provided date time.
+ * Returns the first date time corresponding to this local time that occurs after the provided
+ * date time.
*
* @param compareTime the LocalDateTime to compare against
* @return the next LocalDateTime corresponding to this local time
@@ -434,6 +435,11 @@
return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
}
+ private boolean isDeviceColorManagedInternal() {
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+ return dtm.isDeviceColorManaged();
+ }
+
private abstract class AutoMode implements ColorDisplayController.Callback {
public abstract void onStart();
@@ -616,4 +622,16 @@
return mResultMatrix;
}
}
+
+ private final class BinderService extends IColorDisplayManager.Stub {
+ @Override
+ public boolean isDeviceColorManaged() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return isDeviceColorManagedInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 360a7d1..cf8d21b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -46,6 +46,8 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
import android.hardware.display.DisplayViewport;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
@@ -1241,6 +1243,29 @@
}
}
+ @VisibleForTesting
+ DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal(
+ int displayId) {
+ IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+ return SurfaceControl.getDisplayedContentSamplingAttributes(displayToken);
+ }
+
+ @VisibleForTesting
+ boolean setDisplayedContentSamplingEnabledInternal(
+ int displayId, boolean enable, int componentMask, int maxFrames) {
+ IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+ return SurfaceControl.setDisplayedContentSamplingEnabled(
+ displayToken, enable, componentMask, maxFrames);
+ }
+
+ @VisibleForTesting
+ DisplayedContentSample getDisplayedContentSampleInternal(int displayId,
+ long maxFrames, long timestamp) {
+ IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+ return SurfaceControl.getDisplayedContentSample(
+ displayToken, maxFrames, timestamp);
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
@@ -2331,5 +2356,25 @@
}
}
}
+
+ @Override
+ public DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+ int displayId) {
+ return getDisplayedContentSamplingAttributesInternal(displayId);
+ }
+
+ @Override
+ public boolean setDisplayedContentSamplingEnabled(
+ int displayId, boolean enable, int componentMask, int maxFrames) {
+ return setDisplayedContentSamplingEnabledInternal(
+ displayId, enable, componentMask, maxFrames);
+ }
+
+ @Override
+ public DisplayedContentSample getDisplayedContentSample(int displayId,
+ long maxFrames, long timestamp) {
+ return getDisplayedContentSampleInternal(displayId, maxFrames, timestamp);
+ }
+
}
}
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index d6931e0..4ad26da 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -16,7 +16,6 @@
package com.android.server.display;
-import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.opengl.Matrix;
import android.os.IBinder;
@@ -24,11 +23,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ColorDisplayController;
+
import java.util.Arrays;
/**
@@ -59,10 +59,6 @@
private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015;
private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
-
- private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
- private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
-
/**
* SurfaceFlinger global saturation factor.
*/
@@ -71,6 +67,10 @@
* SurfaceFlinger display color (managed, unmanaged, etc.).
*/
private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023;
+ private static final int SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED = 1030;
+
+ private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
+ private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
private static final float COLOR_SATURATION_NATURAL = 1.0f;
private static final float COLOR_SATURATION_BOOSTED = 1.1f;
@@ -269,6 +269,29 @@
}
/**
+ * Returns whether the screen is color managed via SurfaceFlinger's
+ * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED}.
+ */
+ public boolean isDeviceColorManaged() {
+ final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
+ if (flinger != null) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ try {
+ flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED, data, reply, 0);
+ return reply.readBoolean();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to query wide color support", ex);
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+ return false;
+ }
+
+ /**
* Propagates the provided saturation to the SurfaceFlinger.
*/
private void applySaturation(float saturation) {
@@ -281,7 +304,7 @@
try {
flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
} catch (RemoteException ex) {
- Log.e(TAG, "Failed to set saturation", ex);
+ Slog.e(TAG, "Failed to set saturation", ex);
} finally {
data.recycle();
}
@@ -301,7 +324,7 @@
try {
flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0);
} catch (RemoteException ex) {
- Log.e(TAG, "Failed to set display color", ex);
+ Slog.e(TAG, "Failed to set display color", ex);
} finally {
data.recycle();
}
@@ -312,7 +335,7 @@
try {
ActivityTaskManager.getService().updateConfiguration(null);
} catch (RemoteException e) {
- Log.e(TAG, "Could not update configuration", e);
+ Slog.e(TAG, "Could not update configuration", e);
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index de0f298..25ca278 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1082,13 +1082,14 @@
assertRunOnServiceThread();
if (!canStartArcUpdateAction(message.getSource(), true)) {
- if (getAvrDeviceInfo() == null) {
+ HdmiDeviceInfo avrDeviceInfo = getAvrDeviceInfo();
+ if (avrDeviceInfo == null) {
// AVR may not have been discovered yet. Delay the message processing.
mDelayedMessageBuffer.add(message);
return true;
}
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
- if (!isConnectedToArcPort(message.getSource())) {
+ if (!isConnectedToArcPort(avrDeviceInfo.getPhysicalAddress())) {
displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT);
}
return true;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b148a2f..c0d3fdf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -980,7 +980,7 @@
* @param sourceAddress a logical address of source device where sends polling message
* @param pickStrategy strategy how to pick polling candidates
* @param retryCount the number of retry used to send polling message to remote devices
- * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
+ * @throws IllegalArgumentException if {@code pickStrategy} is invalid value
*/
@ServiceThreadOnly
void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
similarity index 80%
rename from services/core/java/com/android/server/AbstractMasterSystemService.java
rename to services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 9c1e3cd..c0c4a6e 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.infra;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +39,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
import java.io.PrintWriter;
import java.util.List;
@@ -93,6 +96,12 @@
public boolean debug = false;
/**
+ * Whether the service is allowed to bind to an instant-app.
+ */
+ @GuardedBy("mLock")
+ protected boolean mAllowInstantService;
+
+ /**
* Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
* be disabled through {@link UserManager}.
*/
@@ -176,6 +185,107 @@
}
/**
+ * Gets whether the service is allowed to bind to an instant-app.
+ *
+ * <p>Typically called by {@code ShellCommand} during CTS tests.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final boolean getAllowInstantService() {
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ return mAllowInstantService;
+ }
+ }
+
+ /**
+ * Sets whether the service is allowed to bind to an instant-app.
+ *
+ * <p>Typically called by {@code ShellCommand} during CTS tests.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final void setAllowInstantService(boolean mode) {
+ Slog.i(mTag, "setAllowInstantService(): " + mode);
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ mAllowInstantService = mode;
+ }
+ }
+
+ /**
+ * Temporarily sets the service implementation.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @param componentName name of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically reset
+ * to the default component after this timeout expires).
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ * @throws IllegalArgumentException if value of {@code durationMs} is higher than
+ * {@link #getMaximumTemporaryServiceDurationMs()}.
+ */
+ public final void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+ int durationMs) {
+ Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for "
+ + durationMs + "ms");
+ enforceCallingPermissionForManagement();
+
+ Preconditions.checkNotNull(componentName);
+ final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
+ if (durationMs > maxDurationMs) {
+ throw new IllegalArgumentException(
+ "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+ }
+
+ synchronized (mLock) {
+ final S service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.setTemporaryServiceLocked(componentName, durationMs);
+ }
+ }
+ }
+
+ /**
+ * Gets the maximum time the service implementation can be changed.
+ *
+ * @throws UnsupportedOperationException if subclass doesn't override it.
+ */
+ protected int getMaximumTemporaryServiceDurationMs() {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
+
+ /**
+ * Resets the temporary service implementation to the default component.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final void resetTemporaryService(@UserIdInt int userId) {
+ Slog.i(mTag, "resetTemporaryService(): " + userId);
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ final S service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.resetTemporaryServiceLocked();
+ }
+ }
+ }
+
+ /**
+ * Asserts that the caller has permissions to manage this service.
+ *
+ * <p>Typically called by {@code ShellCommand} implementations.
+ *
+ * @throws UnsupportedOperationException if subclass doesn't override it.
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ protected void enforceCallingPermissionForManagement() {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
+
+ /**
* Creates a new service that will be added to the cache.
*
* @param resolvedUserId the resolved user id for the service.
@@ -362,8 +472,11 @@
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
pw.print(" Verbose: "); pw.println(realVerbose);
pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
- pw.print(prefix); pw.print("Settings property: "); pw.println(
- getServiceSettingsProperty());
+ pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
+ final String settingsProperty = getServiceSettingsProperty();
+ if (settingsProperty != null) {
+ pw.print(prefix); pw.print("Settings property: "); pw.println(settingsProperty);
+ }
pw.print(prefix); pw.print("Cached services: ");
if (size == 0) {
pw.println("none");
diff --git a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
similarity index 86%
rename from services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
rename to services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
index f532b22..aaea45e 100644
--- a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.os.IInterface;
import android.util.Slog;
import java.io.PrintWriter;
@@ -29,21 +30,21 @@
* bound.
*
* @param <S> the concrete remote service class
- *
+ * @param <I> the interface of the binder service
* @hide
*/
-public abstract class AbstractMultiplePendingRequestsRemoteService<
- S extends AbstractMultiplePendingRequestsRemoteService<S>>
- extends AbstractRemoteService<S> {
+public abstract class AbstractMultiplePendingRequestsRemoteService<S
+ extends AbstractMultiplePendingRequestsRemoteService<S, I>, I extends IInterface>
+ extends AbstractRemoteService<S, I> {
private final int mInitialCapacity;
- protected ArrayList<PendingRequest<S>> mPendingRequests;
+ protected ArrayList<PendingRequest<S, I>> mPendingRequests;
public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
@NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
- @NonNull VultureCallback callback, boolean bindInstantServiceAllowed, boolean verbose,
- int initialCapacity) {
+ @NonNull VultureCallback<S> callback, boolean bindInstantServiceAllowed,
+ boolean verbose, int initialCapacity) {
super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
verbose);
mInitialCapacity = initialCapacity;
@@ -84,7 +85,7 @@
}
@Override // from AbstractRemoteService
- void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
if (mPendingRequests == null) {
mPendingRequests = new ArrayList<>(mInitialCapacity);
}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
similarity index 84%
rename from services/core/java/com/android/server/AbstractPerUserSystemService.java
rename to services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 001d85f..28a8094 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.infra;
import android.annotation.CallSuper;
import android.annotation.NonNull;
@@ -55,6 +55,8 @@
protected final M mMaster;
+ private final ServiceNameResolver mServiceNameResolver;
+
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
*/
@@ -70,9 +72,13 @@
@GuardedBy("mLock")
private ServiceInfo mServiceInfo;
- protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
+ protected AbstractPerUserSystemService(@NonNull M master,
+ @NonNull ServiceNameResolver serviceNamer, @NonNull Object lock,
@UserIdInt int userId) {
mMaster = master;
+ mServiceNameResolver = serviceNamer;
+ mServiceNameResolver
+ .setOnTemporaryServiceNameChangedCallback(() -> updateLocked(mDisabled));
mLock = lock;
mUserId = userId;
}
@@ -130,7 +136,7 @@
mDisabled = disabled;
ComponentName serviceComponent = null;
ServiceInfo serviceInfo = null;
- final String componentName = getComponentNameFromSettings();
+ final String componentName = getComponentNameLocked();
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -191,13 +197,36 @@
}
/**
- * Gets this name of the remote service this service binds to as defined by {@link Settings}.
+ * Gets the current name of the service, which is either the default service or the
+ * {@link #setTemporaryServiceLocked(String, int) temporary one}.
*/
- @Nullable
- protected final String getComponentNameFromSettings() {
- final String property = mMaster.getServiceSettingsProperty();
- return property == null ? null : Settings.Secure
- .getStringForUser(getContext().getContentResolver(), property, mUserId);
+ protected final String getComponentNameLocked() {
+ return mServiceNameResolver.getServiceNameLocked();
+ }
+
+ /**
+ * Checks whether the current service for the user was temporarily set.
+ */
+ public final boolean isTemporaryServiceSetLocked() {
+ return mServiceNameResolver.isTemporaryLocked();
+ }
+
+ /**
+ * Temporary sets the service implementation.
+ *
+ * @param componentName name of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically reset
+ * to the default component after this timeout expires).
+ */
+ protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+ mServiceNameResolver.setTemporaryServiceLocked(componentName, durationMs);
+ }
+
+ /**
+ * Resets the temporary service implementation to the default component.
+ */
+ protected final void resetTemporaryServiceLocked() {
+ mServiceNameResolver.resetTemporaryServiceLocked();
}
/**
@@ -290,12 +319,7 @@
pw.print(prefix); pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
- final String componentName = getComponentNameFromSettings();
- if (componentName != null) {
- pw.print(prefix); pw.print("Service name: ");
- pw.println(componentName);
- } else {
- pw.println("No service package set");
- }
+ pw.print(prefix); pw.print("Name resolver: "); mServiceNameResolver.dumpShortLocked(pw);
+ pw.println();
}
}
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/infra/AbstractRemoteService.java
similarity index 76%
rename from services/core/java/com/android/server/AbstractRemoteService.java
rename to services/core/java/com/android/server/infra/AbstractRemoteService.java
index f636487..41dcf89 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractRemoteService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.infra;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -33,6 +33,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.FgThread;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -53,13 +54,13 @@
* (no pun intended) example of how to use it.
*
* @param <S> the concrete remote service class
+ * @param <I> the interface of the binder service
*
* @hide
*/
//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
-public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
- implements DeathRecipient {
-
+public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> implements DeathRecipient {
private static final int MSG_UNBIND = 1;
protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
@@ -73,11 +74,11 @@
private final Context mContext;
private final Intent mIntent;
- private final VultureCallback mVultureCallback;
+ private final VultureCallback<S> mVultureCallback;
private final int mUserId;
private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
private final boolean mBindInstantServiceAllowed;
- private IInterface mServiceInterface;
+ protected I mService;
private boolean mBinding;
private boolean mDestroyed;
@@ -86,19 +87,21 @@
/**
* Callback called when the service dies.
+ *
+ * @param <T> service class
*/
- public interface VultureCallback {
+ public interface VultureCallback<T> {
/**
* Called when the service dies.
*
* @param service service that died!
*/
- void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service);
+ void onServiceDied(T service);
}
// NOTE: must be package-protected so this class is not extend outside
AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
- @NonNull ComponentName componentName, int userId, @NonNull VultureCallback callback,
+ @NonNull ComponentName componentName, int userId, @NonNull VultureCallback<S> callback,
boolean bindInstantServiceAllowed, boolean verbose) {
mContext = context;
mVultureCallback = callback;
@@ -149,7 +152,7 @@
* Gets the base Binder interface from the service.
*/
@NonNull
- protected abstract IInterface getServiceInterface(@NonNull IBinder service);
+ protected abstract I getServiceInterface(@NonNull IBinder service);
/**
* Defines How long after the last interaction with the service we would unbind.
@@ -182,12 +185,14 @@
private void handleBinderDied() {
if (checkIfDestroyed()) return;
- if (mServiceInterface != null) {
- mServiceInterface.asBinder().unlinkToDeath(this, 0);
+ if (mService != null) {
+ mService.asBinder().unlinkToDeath(this, 0);
}
- mServiceInterface = null;
+ mService = null;
mServiceDied = true;
- mVultureCallback.onServiceDied(this);
+ @SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
+ final S castService = (S) this;
+ mVultureCallback.onServiceDied(castService);
}
// Note: we are dumping without a lock held so this is a bit racy but
@@ -215,12 +220,35 @@
pw.println();
}
- protected void scheduleRequest(@NonNull PendingRequest<S> pendingRequest) {
+ /**
+ * Schedules a "sync" request.
+ *
+ * <p>This request must be responded by the service somehow (typically using a callback),
+ * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
+ * service doesn't respond.
+ */
+ protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
+ cancelScheduledUnbind();
mHandler.sendMessage(obtainMessage(
AbstractRemoteService::handlePendingRequest, this, pendingRequest));
}
- protected void cancelScheduledUnbind() {
+ /**
+ * Schedules an async request.
+ *
+ * <p>This request is not expecting a callback from the service, hence it's represented by
+ * a simple {@link Runnable}.
+ */
+ protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) {
+ cancelScheduledUnbind();
+ // TODO(b/117779333): fix generics below
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
+ mHandler.sendMessage(
+ obtainMessage(AbstractRemoteService::handlePendingRequest, this, asyncRequest));
+ }
+
+ private void cancelScheduledUnbind() {
mHandler.removeMessages(MSG_UNBIND);
}
@@ -243,11 +271,11 @@
* Handles a request, either processing it right now when bound, or saving it to be handled when
* bound.
*/
- protected final void handlePendingRequest(@NonNull PendingRequest<S> pendingRequest) {
+ protected final void handlePendingRequest(@NonNull PendingRequest<S, I> pendingRequest) {
if (checkIfDestroyed() || mCompleted) return;
if (!handleIsBound()) {
- if (mVerbose) Slog.v(mTag, "handlePendingRequest(): queuing" + pendingRequest);
+ if (mVerbose) Slog.v(mTag, "handlePendingRequest(): queuing " + pendingRequest);
handlePendingRequestWhileUnBound(pendingRequest);
handleEnsureBound();
} else {
@@ -262,10 +290,10 @@
/**
* Defines what to do with a request that arrives while not bound to the service.
*/
- abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest);
+ abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest);
private boolean handleIsBound() {
- return mServiceInterface != null;
+ return mService != null;
}
private void handleEnsureBound() {
@@ -299,9 +327,9 @@
mBinding = false;
if (handleIsBound()) {
handleOnConnectedStateChangedInternal(false);
- if (mServiceInterface != null) {
- mServiceInterface.asBinder().unlinkToDeath(this, 0);
- mServiceInterface = null;
+ if (mService != null) {
+ mService.asBinder().unlinkToDeath(this, 0);
+ mService = null;
}
}
mContext.unbindService(mServiceConnection);
@@ -317,7 +345,7 @@
return;
}
mBinding = false;
- mServiceInterface = getServiceInterface(service);
+ mService = getServiceInterface(service);
try {
service.linkToDeath(AbstractRemoteService.this, 0);
} catch (RemoteException re) {
@@ -331,7 +359,7 @@
@Override
public void onServiceDisconnected(ComponentName name) {
mBinding = true;
- mServiceInterface = null;
+ mService = null;
}
}
@@ -348,10 +376,15 @@
/**
* Base class for the requests serviced by the remote service.
*
+ * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
+ * communicate back with the system server. For cases where that's not needed, you should use
+ * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
+ *
* @param <S> the remote service class
+ * @param <I> the interface of the binder service
*/
- public abstract static class PendingRequest<S extends AbstractRemoteService<S>>
- implements Runnable {
+ public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> implements Runnable {
protected final String mTag = getClass().getSimpleName();
protected final Object mLock = new Object();
@@ -365,7 +398,7 @@
@GuardedBy("mLock")
private boolean mCompleted;
- protected PendingRequest(S service) {
+ protected PendingRequest(@NonNull S service) {
mWeakService = new WeakReference<>(service);
mServiceHandler = service.mHandler;
mTimeoutTrigger = () -> {
@@ -378,6 +411,7 @@
final S remoteService = mWeakService.get();
if (remoteService != null) {
+ // TODO(b/117779333): we should probably ignore it if service is destroyed.
Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
onTimeout(remoteService);
} else {
@@ -450,4 +484,50 @@
return false;
}
}
+
+ /**
+ * Represents a request that does not expect a callback from the remote service.
+ *
+ * @param <I> the interface of the binder service
+ */
+ public interface AsyncRequest<I extends IInterface> {
+
+ /**
+ * Run Forrest, run!
+ */
+ void run(@NonNull I binder) throws RemoteException;
+ }
+
+ private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> extends PendingRequest<S, I> {
+ private static final String TAG = MyAsyncPendingRequest.class.getSimpleName();
+
+ private final AsyncRequest<I> mRequest;
+
+ protected MyAsyncPendingRequest(@NonNull S service, @NonNull AsyncRequest<I> request) {
+ super(service);
+
+ mRequest = request;
+ }
+
+ @Override
+ public void run() {
+ final S remoteService = getService();
+ if (remoteService == null) return;
+ try {
+ mRequest.run(remoteService.mService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception handling async request (" + this + "): " + e);
+ } finally {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onTimeout(S remoteService) {
+ // TODO(b/117779333): should not happen because we called finish() on run(), although
+ // currently it might be called if the service is destroyed while showing it.
+ Slog.w(TAG, "AsyncPending requested timed out");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
similarity index 81%
rename from services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
rename to services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
index 8e1f540..d32f13b 100644
--- a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.os.IInterface;
import android.util.Slog;
import java.io.PrintWriter;
@@ -29,17 +30,19 @@
* <p>If another request is received while not bound, the previous one will be canceled.
*
* @param <S> the concrete remote service class
+ * @param <I> the interface of the binder service
*
* @hide
*/
-public abstract class AbstractSinglePendingRequestRemoteService<
- S extends AbstractSinglePendingRequestRemoteService<S>> extends AbstractRemoteService<S> {
+public abstract class AbstractSinglePendingRequestRemoteService<S
+ extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface>
+ extends AbstractRemoteService<S, I> {
- protected PendingRequest<S> mPendingRequest;
+ protected PendingRequest<S, I> mPendingRequest;
public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
@NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
- @NonNull VultureCallback callback, boolean bindInstantServiceAllowed,
+ @NonNull VultureCallback<S> callback, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
verbose);
@@ -48,7 +51,7 @@
@Override // from AbstractRemoteService
void handlePendingRequests() {
if (mPendingRequest != null) {
- final PendingRequest<S> pendingRequest = mPendingRequest;
+ final PendingRequest<S, I> pendingRequest = mPendingRequest;
mPendingRequest = null;
handlePendingRequest(pendingRequest);
}
@@ -70,10 +73,11 @@
}
@Override // from AbstractRemoteService
- void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
if (mPendingRequest != null) {
if (mVerbose) {
- Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest);
+ Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest
+ + " to handle " + pendingRequest);
}
mPendingRequest.cancel();
}
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
new file mode 100644
index 0000000..27822d5
--- /dev/null
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.infra;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * Gets the service name using a framework resources, temporarily changing the service if necessary
+ * (typically during CTS tests or service development).
+ *
+ * @hide
+ */
+public final class FrameworkResourcesServiceNameResolver implements ServiceNameResolver {
+
+ private static final String TAG = FrameworkResourcesServiceNameResolver.class.getSimpleName();
+
+ /** Handler message to {@link #resetTemporaryServiceLocked()} */
+ private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
+
+ private final @NonNull Context mContext;
+ private final @NonNull @UserIdInt int mUserId;
+ private final @NonNull Object mLock;
+ private final @StringRes int mResourceId;
+ private @Nullable Runnable mOnSetCallback;
+
+ /**
+ * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private String mTemporaryServiceName;
+
+ /**
+ * When the temporary service will expire (and reset back to the default).
+ */
+ @GuardedBy("mLock")
+ private long mTemporaryServiceExpiration;
+
+ /**
+ * Handler used to reset the temporary service name.
+ */
+ @GuardedBy("mLock")
+ private Handler mTemporaryHandler;
+
+ public FrameworkResourcesServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
+ @NonNull Object lock, @StringRes int resourceId) {
+ mLock = lock;
+ mContext = context;
+ mUserId = userId;
+ mResourceId = resourceId;
+ }
+
+ @Override
+ public void setOnTemporaryServiceNameChangedCallback(@NonNull Runnable callback) {
+ this.mOnSetCallback = callback;
+ }
+
+ @Override
+ public String getDefaultServiceName() {
+ final String name = mContext.getString(mResourceId);
+ return TextUtils.isEmpty(name) ? null : name;
+ }
+
+ @Override
+ public String getServiceNameLocked() {
+ if (mTemporaryServiceName != null) {
+ // Always log it, as it should only be used on CTS or during development
+ Slog.w(TAG, "getComponentName(): using temporary name " + mTemporaryServiceName);
+ return mTemporaryServiceName;
+ } else {
+ return getDefaultServiceName();
+ }
+ }
+
+ @Override
+ public boolean isTemporaryLocked() {
+ return mTemporaryServiceName != null;
+ }
+
+ @Override
+ public void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+ mTemporaryServiceName = componentName;
+
+ if (mTemporaryHandler == null) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+ synchronized (mLock) {
+ resetTemporaryServiceLocked();
+ }
+ } else {
+ Slog.wtf(TAG, "invalid handler msg: " + msg);
+ }
+ }
+ };
+ } else {
+ removeResetTemporaryServiceMessageLocked();
+ }
+ mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
+ mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+ onServiceNameChanged();
+ }
+
+ @Override
+ public void resetTemporaryServiceLocked() {
+ Slog.i(TAG, "resetting temporary service from " + mTemporaryServiceName);
+ mTemporaryServiceName = null;
+ if (mTemporaryHandler != null) {
+ removeResetTemporaryServiceMessageLocked();
+ mTemporaryHandler = null;
+ }
+ onServiceNameChanged();
+ }
+
+ // TODO(b/117779333): support proto
+ @Override
+ public void dumpShortLocked(@NonNull PrintWriter pw) {
+ pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
+ if (mTemporaryServiceName != null) {
+ pw.print(", tmpName="); pw.print(mTemporaryServiceName);
+ final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
+ pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print(")");
+ pw.print(", defaultName="); pw.println(getDefaultServiceName());
+ } else {
+ pw.print(", serviceName="); pw.println(getDefaultServiceName());
+ }
+ }
+
+ private void onServiceNameChanged() {
+ if (mOnSetCallback != null) {
+ mOnSetCallback.run();
+ }
+ }
+
+ private void removeResetTemporaryServiceMessageLocked() {
+ // NOTE: caller should already have checked it
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+ }
+}
diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
new file mode 100644
index 0000000..7af8f60
--- /dev/null
+++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.infra;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.provider.Settings;
+
+import java.io.PrintWriter;
+
+/**
+ * Gets the service name using a property from the {@link android.provider.Settings.Secure}
+ * provider.
+ *
+ * @hide
+ */
+public final class SecureSettingsServiceNameResolver implements ServiceNameResolver {
+
+ private final @NonNull Context mContext;
+ private final @NonNull @UserIdInt int mUserId;
+
+ @NonNull
+ private final String mProperty;
+
+ public SecureSettingsServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
+ @NonNull String property) {
+ mContext = context;
+ mUserId = userId;
+ mProperty = property;
+ }
+
+ @Override
+ public String getDefaultServiceName() {
+ return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, mUserId);
+ }
+
+ // TODO(b/117779333): support proto
+ @Override
+ public void dumpShortLocked(@NonNull PrintWriter pw) {
+ pw.print("SecureSettingsServiceNamer: prop="); pw.print(mProperty);
+ pw.print(", value="); pw.println(getDefaultServiceName());
+ }
+}
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
new file mode 100644
index 0000000..205d40b
--- /dev/null
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.infra;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.PrintWriter;
+
+/**
+ * A helper class used to resolve the name of the app-provided service a
+ * {@link AbstractRemoteService} binds to.
+ *
+ * @hide
+ */
+public interface ServiceNameResolver {
+
+ /**
+ * Sets a callback that is called after the service is
+ * {@link #setTemporaryServiceLocked(String, int) set} or
+ * {@link #resetTemporaryServiceLocked() reset}.
+ *
+ * <p>Typically called after the object is constructed.
+ */
+ default void setOnTemporaryServiceNameChangedCallback(
+ @SuppressWarnings("unused") @NonNull Runnable callback) {
+ // ignored by default
+ }
+
+ /**
+ * Gets the default name of the service.
+ *
+ * <p>Typically implemented by reading a Settings property or framework resource.
+ */
+ @Nullable
+ String getDefaultServiceName();
+
+ /**
+ * Gets the current name of the service.
+ *
+ * <p>Must be called after acquiring this object's lock.
+ *
+ * @return either the temporary name (set by {@link #setTemporaryServiceLocked(String, int)}, or
+ * the {@link #getDefaultServiceName() default name}.
+ */
+ @Nullable
+ default String getServiceNameLocked() {
+ return getDefaultServiceName();
+ }
+
+ /**
+ * Checks whether the current service is temporary.
+ *
+ * <p>Must be called after acquiring this object's lock.
+ */
+ default boolean isTemporaryLocked() {
+ return false;
+ }
+
+ /**
+ * Temporarily sets the service implementation.
+ *
+ * <p>Must be called after acquiring this object's lock.
+ *
+ * @param componentName name of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically reset
+ * to the default component after this timeout expires).
+ *
+ * @throws UnsupportedOperationException if not implemented.
+ */
+ default void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+ throw new UnsupportedOperationException("temporary user not supported");
+ }
+
+ /**
+ * Resets the temporary service implementation to the default component.
+ *
+ * <p>Must be called after acquiring this object's lock.
+ *
+ * @throws UnsupportedOperationException if not implemented.
+ */
+ default void resetTemporaryServiceLocked() {
+ throw new UnsupportedOperationException("temporary user not supported");
+ }
+
+ /**
+ * Dump this object in just one line (without calling {@code println}.
+ *
+ * <p>Must be called after acquiring this object's lock.
+ */
+ // TODO(b/117779333): support proto
+ void dumpShortLocked(@NonNull PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/infra/package.html b/services/core/java/com/android/server/infra/package.html
new file mode 100644
index 0000000..61a4c43
--- /dev/null
+++ b/services/core/java/com/android/server/infra/package.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+Contains common classes providing the plumbing infrastructure necessary to implement a system
+service
+</body>
+</html>
\ No newline at end of file
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d96b6cb..e7c3c7b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,6 +1951,11 @@
}
// Native callback.
+ private int getPointerDisplayId() {
+ return mWindowManagerCallbacks.getPointerDisplayId();
+ }
+
+ // Native callback.
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
@@ -2017,6 +2022,8 @@
KeyEvent event, int policyFlags);
public int getPointerLayer();
+
+ public int getPointerDisplayId();
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a8da968..28a6ba4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -984,9 +984,10 @@
// {@link #canShowInputMethodPickerLocked(IInputMethodClient)}.
mHandler.obtainMessage(
MSG_SHOW_IM_SUBTYPE_PICKER,
+ // TODO(b/120076400): Design and implement IME switcher for heterogeneous
+ // navbar configuration.
InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES,
- 0 /* arg2 */)
- .sendToTarget();
+ DEFAULT_DISPLAY).sendToTarget();
} else {
Slog.w(TAG, "Unexpected intent " + intent);
}
@@ -1617,7 +1618,8 @@
// Check whether or not this is a valid IPC. Assumes an IPC is valid when either
// 1) it comes from the system process
// 2) the calling process' user id is identical to the current user id IMMS thinks.
- private boolean calledFromValidUser() {
+ @GuardedBy("mMethodMap")
+ private boolean calledFromValidUserLocked() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
if (DEBUG) {
@@ -1657,7 +1659,8 @@
* @param token The window token given to the input method when it was started.
* @return true if and only if non-null valid token is specified.
*/
- private boolean calledWithValidToken(@Nullable IBinder token) {
+ @GuardedBy("mMethodMap")
+ private boolean calledWithValidTokenLocked(@Nullable IBinder token) {
if (token == null && Binder.getCallingPid() == Process.myPid()) {
if (DEBUG) {
// TODO(b/34851776): Basically it's the caller's fault if we reach here.
@@ -1698,11 +1701,11 @@
}
private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUser()) {
- return Collections.emptyList();
- }
synchronized (mMethodMap) {
+ // TODO: Make this work even for non-current users?
+ if (!calledFromValidUserLocked()) {
+ return Collections.emptyList();
+ }
ArrayList<InputMethodInfo> methodList = new ArrayList<>();
for (InputMethodInfo info : mMethodList) {
@@ -1716,11 +1719,11 @@
@Override
public List<InputMethodInfo> getEnabledInputMethodList() {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUser()) {
- return Collections.emptyList();
- }
synchronized (mMethodMap) {
+ // TODO: Make this work even for non-current users?
+ if (!calledFromValidUserLocked()) {
+ return Collections.emptyList();
+ }
return mSettings.getEnabledInputMethodListLocked();
}
}
@@ -1732,11 +1735,11 @@
@Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
boolean allowsImplicitlySelectedSubtypes) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUser()) {
- return Collections.emptyList();
- }
synchronized (mMethodMap) {
+ // TODO: Make this work even for non-current users?
+ if (!calledFromValidUserLocked()) {
+ return Collections.emptyList();
+ }
final InputMethodInfo imi;
if (imiId == null && mCurMethodId != null) {
imi = mMethodMap.get(mCurMethodId);
@@ -2238,7 +2241,7 @@
private void updateStatusIcon(@NonNull IBinder token, String packageName,
@DrawableRes int iconId) {
synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
+ if (!calledWithValidTokenLocked(token)) {
return;
}
final long ident = Binder.clearCallingIdentity();
@@ -2341,11 +2344,10 @@
@BinderThread
@SuppressWarnings("deprecation")
private void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
- if (!calledWithValidToken(token)) {
- return;
- }
-
synchronized (mMethodMap) {
+ if (!calledWithValidTokenLocked(token)) {
+ return;
+ }
mImeWindowVis = vis;
mBackDisposition = backDisposition;
updateSystemUiLocked(token, vis, backDisposition);
@@ -2376,11 +2378,10 @@
@BinderThread
private void reportStartInput(IBinder token, IBinder startInputToken) {
- if (!calledWithValidToken(token)) {
- return;
- }
-
synchronized (mMethodMap) {
+ if (!calledWithValidTokenLocked(token)) {
+ return;
+ }
final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
@@ -2391,7 +2392,7 @@
// Caution! This method is called in this class. Handle multi-user carefully
private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
- if (!calledWithValidToken(token)) {
+ if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -2450,10 +2451,10 @@
@Override
public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
- if (!calledFromValidUser()) {
- return;
- }
synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return;
+ }
final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
for (int i = 0; i < spans.length; ++i) {
SuggestionSpan ss = spans[i];
@@ -2466,10 +2467,10 @@
@Override
public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
- if (!calledFromValidUser()) {
- return false;
- }
synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return false;
+ }
final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
// TODO: Do not send the intent if the process of the targetImi is already dead.
if (targetImi != null) {
@@ -2633,13 +2634,13 @@
@Override
public boolean showSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
- if (!calledFromValidUser()) {
- return false;
- }
int uid = Binder.getCallingUid();
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mMethodMap) {
+ synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return false;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
// We need to check if this is the current client with
@@ -2657,9 +2658,9 @@
}
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
return showCurrentInputLocked(flags, resultReceiver);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
@@ -2718,13 +2719,13 @@
@Override
public boolean hideSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
- if (!calledFromValidUser()) {
- return false;
- }
int uid = Binder.getCallingUid();
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mMethodMap) {
+ synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return false;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
// We need to check if this is the current client with
@@ -2745,9 +2746,9 @@
if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
return hideCurrentInputLocked(flags, resultReceiver);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
@@ -2827,14 +2828,14 @@
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
IInputContext inputContext, @MissingMethodFlags int missingMethods,
int unverifiedTargetSdkVersion) {
- // Needs to check the validity before clearing calling identity
- final boolean calledFromValidUser = calledFromValidUser();
InputBindResult res = null;
- long ident = Binder.clearCallingIdentity();
- try {
+ synchronized (mMethodMap) {
+ // Needs to check the validity before clearing calling identity
+ final boolean calledFromValidUser = calledFromValidUserLocked();
final int windowDisplayId =
mWindowManagerInternal.getDisplayIdForWindow(windowToken);
- synchronized (mMethodMap) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
+ " client=" + client.asBinder()
@@ -3023,9 +3024,9 @@
res = InputBindResult.NULL_EDITOR_INFO;
}
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
return res;
@@ -3034,9 +3035,7 @@
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
// TODO(yukawa): multi-display support.
final int uid = Binder.getCallingUid();
- if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
- return true;
- } else if (mCurFocusedWindowClient != null && client != null
+ if (mCurFocusedWindowClient != null && client != null
&& mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
return true;
} else if (mCurIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
@@ -3044,22 +3043,17 @@
uid,
mCurIntent.getComponent().getPackageName())) {
return true;
- } else if (mContext.checkCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
}
-
return false;
}
@Override
public void showInputMethodPickerFromClient(
IInputMethodClient client, int auxiliarySubtypeMode) {
- if (!calledFromValidUser()) {
- return;
- }
synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return;
+ }
if(!canShowInputMethodPickerLocked(client)) {
Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
+ Binder.getCallingUid() + ": " + client);
@@ -3068,11 +3062,26 @@
// Always call subtype picker, because subtype picker is a superset of input method
// picker.
- mHandler.sendMessage(mCaller.obtainMessageI(
- MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode));
+ mHandler.sendMessage(mCaller.obtainMessageII(
+ MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode,
+ (mCurClient != null) ? mCurClient.selfReportedDisplayId : DEFAULT_DISPLAY));
}
}
+ @Override
+ public void showInputMethodPickerFromSystem(IInputMethodClient client, int auxiliarySubtypeMode,
+ int displayId) {
+ if (mContext.checkCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "showInputMethodPickerFromSystem requires WRITE_SECURE_SETTINGS permission");
+ }
+ // Always call subtype picker, because subtype picker is a superset of input method
+ // picker.
+ mHandler.sendMessage(mCaller.obtainMessageII(
+ MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId));
+ }
+
public boolean isInputMethodPickerShownForTest() {
synchronized(mMethodMap) {
if (mSwitchingDialog == null) {
@@ -3084,18 +3093,20 @@
@Override
public void setInputMethod(IBinder token, String id) {
- if (!calledFromValidUser()) {
- return;
+ synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return;
+ }
+ setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID);
}
- setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
}
@Override
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
- if (!calledFromValidUser()) {
- return;
- }
synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return;
+ }
if (subtype != null) {
setInputMethodWithSubtypeIdLocked(token, id,
InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
@@ -3109,22 +3120,22 @@
@Override
public void showInputMethodAndSubtypeEnablerFromClient(
IInputMethodClient client, String inputMethodId) {
- // TODO(yukawa): Should we verify the display ID?
- if (!calledFromValidUser()) {
- return;
- }
synchronized (mMethodMap) {
+ // TODO(yukawa): Should we verify the display ID?
+ if (!calledFromValidUserLocked()) {
+ return;
+ }
executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
}
}
- @Override
- public boolean switchToPreviousInputMethod(IBinder token) {
- if (!calledFromValidUser()) {
- return false;
- }
+ @BinderThread
+ private boolean switchToPreviousInputMethod(IBinder token) {
synchronized (mMethodMap) {
+ if (!calledWithValidTokenLocked(token)) {
+ return false;
+ }
final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
final InputMethodInfo lastImi;
if (lastIme != null) {
@@ -3191,13 +3202,10 @@
}
}
- @Override
- public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
- if (!calledFromValidUser()) {
- return false;
- }
+ @BinderThread
+ private boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
+ if (!calledWithValidTokenLocked(token)) {
return false;
}
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
@@ -3213,11 +3221,8 @@
@BinderThread
private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
- if (!calledFromValidUser()) {
- return false;
- }
synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
+ if (!calledWithValidTokenLocked(token)) {
return false;
}
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
@@ -3231,10 +3236,10 @@
@Override
public InputMethodSubtype getLastInputMethodSubtype() {
- if (!calledFromValidUser()) {
- return null;
- }
synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return null;
+ }
final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
// TODO: Handle the case of the last IME with no subtypes
if (lastIme == null || TextUtils.isEmpty(lastIme.first)
@@ -3257,13 +3262,13 @@
@Override
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
- if (!calledFromValidUser()) {
- return;
- }
// By this IPC call, only a process which shares the same uid with the IME can add
// additional input method subtypes to the IME.
if (TextUtils.isEmpty(imiId) || subtypes == null) return;
synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return;
+ }
if (!mSystemReady) {
return;
}
@@ -3329,12 +3334,6 @@
}
}
- private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
- synchronized (mMethodMap) {
- setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
- }
- }
-
private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -3360,11 +3359,8 @@
@BinderThread
private void hideMySoftInput(@NonNull IBinder token, int flags) {
- if (!calledFromValidUser()) {
- return;
- }
synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
+ if (!calledWithValidTokenLocked(token)) {
return;
}
long ident = Binder.clearCallingIdentity();
@@ -3378,11 +3374,8 @@
@BinderThread
private void showMySoftInput(@NonNull IBinder token, int flags) {
- if (!calledFromValidUser()) {
- return;
- }
synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
+ if (!calledWithValidTokenLocked(token)) {
return;
}
long ident = Binder.clearCallingIdentity();
@@ -3421,6 +3414,7 @@
switch (msg.what) {
case MSG_SHOW_IM_SUBTYPE_PICKER:
final boolean showAuxSubtypes;
+ final int displayId = msg.arg2;
switch (msg.arg1) {
case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO:
// This is undocumented so far, but IMM#showInputMethodPicker() has been
@@ -3438,7 +3432,7 @@
Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
return false;
}
- showInputMethodMenu(showAuxSubtypes);
+ showInputMethodMenu(showAuxSubtypes, displayId);
return true;
case MSG_SHOW_IM_SUBTYPE_ENABLER:
@@ -3818,7 +3812,7 @@
&& mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
}
- private void showInputMethodMenu(boolean showAuxSubtypes) {
+ private void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
final boolean isScreenLocked = isScreenLocked();
@@ -3864,8 +3858,10 @@
}
}
+ final ActivityThread currentThread = ActivityThread.currentActivityThread();
final Context settingsContext = new ContextThemeWrapper(
- ActivityThread.currentActivityThread().getSystemUiContext(),
+ displayId == DEFAULT_DISPLAY ? currentThread.getSystemUiContext()
+ : currentThread.createSystemUiContext(displayId),
com.android.internal.R.style.Theme_DeviceDefault_Settings);
mDialogBuilder = new AlertDialog.Builder(settingsContext);
@@ -4195,11 +4191,11 @@
*/
@Override
public InputMethodSubtype getCurrentInputMethodSubtype() {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUser()) {
- return null;
- }
synchronized (mMethodMap) {
+ // TODO: Make this work even for non-current users?
+ if (!calledFromValidUserLocked()) {
+ return null;
+ }
return getCurrentInputMethodSubtypeLocked();
}
}
@@ -4274,11 +4270,11 @@
@Override
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUser()) {
- return false;
- }
synchronized (mMethodMap) {
+ // TODO: Make this work even for non-current users?
+ if (!calledFromValidUserLocked()) {
+ return false;
+ }
if (subtype != null && mCurMethodId != null) {
InputMethodInfo imi = mMethodMap.get(mCurMethodId);
int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode());
@@ -4532,10 +4528,6 @@
@BinderThread
private IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
@Nullable Uri contentUri, @Nullable String packageName) {
- if (!calledFromValidUser()) {
- return null;
- }
-
if (token == null) {
throw new NullPointerException("token");
}
@@ -4589,11 +4581,8 @@
@BinderThread
private void reportFullscreenMode(IBinder token, boolean fullscreen) {
- if (!calledFromValidUser()) {
- return;
- }
synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
+ if (!calledWithValidTokenLocked(token)) {
return;
}
if (mCurClient != null && mCurClient.client != null) {
@@ -4933,11 +4922,6 @@
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
private int handleShellCommandEnableDisableInputMethod(
@NonNull ShellCommand shellCommand, boolean enabled) {
- if (!calledFromValidUser()) {
- shellCommand.getErrPrintWriter().print(
- "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
- return ShellCommandResult.FAILURE;
- }
final String id = shellCommand.getNextArgRequired();
final boolean previouslyEnabled;
@@ -4994,11 +4978,6 @@
@ShellCommandResult
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
- if (!calledFromValidUser()) {
- shellCommand.getErrPrintWriter().print(
- "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
- return ShellCommandResult.FAILURE;
- }
synchronized (mMethodMap) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS)
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 2b67fe7..5edb5c8 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1541,6 +1541,13 @@
@BinderThread
@Override
+ public void showInputMethodPickerFromSystem(
+ IInputMethodClient client, int auxiliarySubtypeMode, int displayId) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
public void showInputMethodAndSubtypeEnablerFromClient(
IInputMethodClient client, String inputMethodId) {
reportNotSupported();
@@ -1595,20 +1602,6 @@
@BinderThread
@Override
- public boolean switchToPreviousInputMethod(IBinder token) {
- reportNotSupported();
- return false;
- }
-
- @BinderThread
- @Override
- public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
- reportNotSupported();
- return false;
- }
-
- @BinderThread
- @Override
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
reportNotSupported();
}
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
deleted file mode 100644
index d5be26a..0000000
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.intelligence;
-
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
-
-/**
- * Intelligence Manager local system service interface.
- *
- * @hide Only for use within the system server.
- */
-//TODO(b/111276913): rename once the final name is defined
-public abstract class IntelligenceManagerInternal {
-
- /**
- * Checks whether the given {@code uid} owns the
- * {@link android.service.intelligence.SmartSuggestionsService} implementation associated with
- * the given {@code userId}.
- */
- public abstract boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId);
-
- /**
- * Notifies the intelligence service of new assist data for the given activity.
- *
- * @return {@code false} if there was no service set for the given user
- */
- public abstract boolean sendActivityAssistData(@UserIdInt int userId,
- @NonNull IBinder activityToken, @NonNull Bundle data);
-
- /**
- * Asks the intelligence service to provide Augmented Autofill for a given activity.
- *
- * @param userId user handle
- * @param client binder used to communicate with the activity that originated this request.
- * @param activityToken activity that originated this request.
- * @param autofillSessionId autofill session id (must be used on {@code client} calls.
- * @param focusedId id of the the field that triggered this request.
- *
- * @return {@code false} if the service cannot handle this request, {@code true} otherwise.
- * <b>NOTE: </b> it must return right away; typically it will return {@code false} if the
- * service is disabled (or the activity blacklisted).
- */
- public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
- @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
- int autofillSessionId, @NonNull AutofillId focusedId);
-
- /**
- * Callback used by the Autofill Session to communicate with the Augmented Autofill service.
- */
- public interface AugmentedAutofillCallback {
- // TODO(b/111330312): this method is calling when the Autofill session is destroyed, the
- // main reason being the cases where user tap HOME.
- // Right now it's completely destroying the UI, but we need to decide whether / how to
- // properly recover it later (for example, if the user switches back to the activity,
- // should it be restored? Right not it kind of is, because Autofill's Session trigger a
- // new FillRequest, which in turn triggers the Augmented Autofill request again)
- /**
- * Destroys the Autofill UI.
- */
- void destroy();
- }
-}
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
new file mode 100644
index 0000000..ee1d847
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job;
+
+import android.app.ActivityManager;
+import android.app.job.JobInfo;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.server.job.controllers.JobStatus;
+import com.android.server.job.controllers.StateController;
+
+import java.util.Iterator;
+import java.util.List;
+
+class JobConcurrencyManager {
+ private static final String TAG = JobSchedulerService.TAG;
+ private static final boolean DEBUG = JobSchedulerService.DEBUG;
+
+ private final Object mLock;
+ private final JobSchedulerService mService;
+ private final JobSchedulerService.Constants mConstants;
+
+ private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
+
+ /**
+ * This array essentially stores the state of mActiveServices array.
+ * The ith index stores the job present on the ith JobServiceContext.
+ * We manipulate this array until we arrive at what jobs should be running on
+ * what JobServiceContext.
+ */
+ JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+
+ /**
+ * Indicates whether we need to act on this jobContext id
+ */
+ boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+
+ /**
+ * The uid whose jobs we would like to assign to a context.
+ */
+ int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+
+ JobConcurrencyManager(JobSchedulerService service) {
+ mService = service;
+ mLock = mService.mLock;
+ mConstants = service.mConstants;
+ }
+
+ /**
+ * Takes jobs from pending queue and runs them on available contexts.
+ * If no contexts are available, preempts lower priority jobs to
+ * run higher priority ones.
+ * Lock on mJobs before calling this function.
+ */
+ void assignJobsToContextsLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, printPendingQueueLocked());
+ }
+
+ final JobPackageTracker tracker = mService.mJobPackageTracker;
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ final List<JobServiceContext> activeServices = mService.mActiveServices;
+ final List<StateController> controllers = mService.mControllers;
+
+ int memLevel;
+ try {
+ memLevel = ActivityManager.getService().getMemoryTrimLevel();
+ } catch (RemoteException e) {
+ memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ }
+ switch (memLevel) {
+ case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+ mService.mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ mService.mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ mService.mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
+ break;
+ default:
+ mService.mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
+ break;
+ }
+
+ JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
+ boolean[] act = mTmpAssignAct;
+ int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
+ int numActive = 0;
+ int numForeground = 0;
+ for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
+ final JobServiceContext js = mService.mActiveServices.get(i);
+ final JobStatus status = js.getRunningJobLocked();
+ if ((contextIdToJobMap[i] = status) != null) {
+ numActive++;
+ if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+ numForeground++;
+ }
+ }
+ act[i] = false;
+ preferredUidForContext[i] = js.getPreferredUid();
+ }
+ if (DEBUG) {
+ Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
+ }
+ for (int i=0; i<pendingJobs.size(); i++) {
+ JobStatus nextPending = pendingJobs.get(i);
+
+ // If job is already running, go to next job.
+ int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
+ if (jobRunningContext != -1) {
+ continue;
+ }
+
+ final int priority = mService.evaluateJobPriorityLocked(nextPending);
+ nextPending.lastEvaluatedPriority = priority;
+
+ // Find a context for nextPending. The context should be available OR
+ // it should have lowest priority among all running jobs
+ // (sharing the same Uid as nextPending)
+ int minPriority = Integer.MAX_VALUE;
+ int minPriorityContextId = -1;
+ for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
+ JobStatus job = contextIdToJobMap[j];
+ int preferredUid = preferredUidForContext[j];
+ if (job == null) {
+ if ((numActive < mService.mMaxActiveJobs ||
+ (priority >= JobInfo.PRIORITY_TOP_APP &&
+ numForeground < mConstants.FG_JOB_COUNT)) &&
+ (preferredUid == nextPending.getUid() ||
+ preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+ // This slot is free, and we haven't yet hit the limit on
+ // concurrent jobs... we can just throw the job in to here.
+ minPriorityContextId = j;
+ break;
+ }
+ // No job on this context, but nextPending can't run here because
+ // the context has a preferred Uid or we have reached the limit on
+ // concurrent jobs.
+ continue;
+ }
+ if (job.getUid() != nextPending.getUid()) {
+ continue;
+ }
+ if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
+ continue;
+ }
+ if (minPriority > nextPending.lastEvaluatedPriority) {
+ minPriority = nextPending.lastEvaluatedPriority;
+ minPriorityContextId = j;
+ }
+ }
+ if (minPriorityContextId != -1) {
+ contextIdToJobMap[minPriorityContextId] = nextPending;
+ act[minPriorityContextId] = true;
+ numActive++;
+ if (priority >= JobInfo.PRIORITY_TOP_APP) {
+ numForeground++;
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
+ }
+ tracker.noteConcurrency(numActive, numForeground);
+ for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
+ boolean preservePreferredUid = false;
+ if (act[i]) {
+ JobStatus js = activeServices.get(i).getRunningJobLocked();
+ if (js != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "preempting job: "
+ + activeServices.get(i).getRunningJobLocked());
+ }
+ // preferredUid will be set to uid of currently running job.
+ activeServices.get(i).preemptExecutingJobLocked();
+ preservePreferredUid = true;
+ } else {
+ final JobStatus pendingJob = contextIdToJobMap[i];
+ if (DEBUG) {
+ Slog.d(TAG, "About to run job on context "
+ + String.valueOf(i) + ", job: " + pendingJob);
+ }
+ for (int ic=0; ic<controllers.size(); ic++) {
+ controllers.get(ic).prepareForExecutionLocked(pendingJob);
+ }
+ if (!activeServices.get(i).executeRunnableJob(pendingJob)) {
+ Slog.d(TAG, "Error executing " + pendingJob);
+ }
+ if (pendingJobs.remove(pendingJob)) {
+ tracker.noteNonpending(pendingJob);
+ }
+ }
+ }
+ if (!preservePreferredUid) {
+ activeServices.get(i).clearPreferredUid();
+ }
+ }
+ }
+
+ int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
+ for (int i=0; i<map.length; i++) {
+ if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private String printPendingQueueLocked() {
+ StringBuilder s = new StringBuilder("Pending queue: ");
+ Iterator<JobStatus> it = mService.mPendingJobs.iterator();
+ while (it.hasNext()) {
+ JobStatus js = it.next();
+ s.append("(")
+ .append(js.getJob().getId())
+ .append(", ")
+ .append(js.getUid())
+ .append(") ");
+ }
+ return s.toString();
+ }
+
+ private String printContextIdToJobMap(JobStatus[] map, String initial) {
+ StringBuilder s = new StringBuilder(initial + ": ");
+ for (int i=0; i<map.length; i++) {
+ s.append("(")
+ .append(map[i] == null? -1: map[i].getJobId())
+ .append(map[i] == null? -1: map[i].getUid())
+ .append(")" );
+ }
+ return s.toString();
+ }
+
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b3f0629..78e18e9 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -54,6 +54,8 @@
import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Handler;
+import android.os.IThermalService;
+import android.os.IThermalStatusListener;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -62,6 +64,7 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
+import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.provider.Settings;
@@ -75,10 +78,9 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -97,6 +99,7 @@
import com.android.server.job.controllers.DeviceIdleJobsController;
import com.android.server.job.controllers.IdleController;
import com.android.server.job.controllers.JobStatus;
+import com.android.server.job.controllers.QuotaController;
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
@@ -111,7 +114,6 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -135,7 +137,7 @@
public static final boolean DEBUG_STANDBY = DEBUG || false;
/** The maximum number of concurrent jobs we run at one time. */
- private static final int MAX_JOB_CONTEXTS_COUNT = 16;
+ static final int MAX_JOB_CONTEXTS_COUNT = 16;
/** Enforce a per-app limit on scheduled jobs? */
private static final boolean ENFORCE_MAX_JOBS = true;
/** The maximum number of jobs that we allow an unprivileged app to schedule */
@@ -156,6 +158,7 @@
final StandbyTracker mStandbyTracker;
/** Tracking amount of time each package runs for. */
final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
+ final JobConcurrencyManager mConcurrencyManager;
static final int MSG_JOB_EXPIRED = 0;
static final int MSG_CHECK_JOB = 1;
@@ -173,13 +176,18 @@
final List<JobServiceContext> mActiveServices = new ArrayList<>();
/** List of controllers that will notify this service of updates to jobs. */
- private final List<StateController> mControllers;
+ final List<StateController> mControllers;
/** Need direct access to this for testing. */
private final BatteryController mBatteryController;
/** Need direct access to this for testing. */
private final StorageController mStorageController;
/** Need directly for sending uid state changes */
private final DeviceIdleJobsController mDeviceIdleJobsController;
+ /** Need directly for receiving thermal events */
+ private IThermalService mThermalService;
+ /** Thermal constraint. */
+ @GuardedBy("mLock")
+ private boolean mThermalConstraint = false;
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -245,11 +253,11 @@
* Named indices into the STANDBY_BEATS array, for clarity in referring to
* specific buckets' bookkeeping.
*/
- static final int ACTIVE_INDEX = 0;
- static final int WORKING_INDEX = 1;
- static final int FREQUENT_INDEX = 2;
- static final int RARE_INDEX = 3;
- static final int NEVER_INDEX = 4;
+ public static final int ACTIVE_INDEX = 0;
+ public static final int WORKING_INDEX = 1;
+ public static final int FREQUENT_INDEX = 2;
+ public static final int RARE_INDEX = 3;
+ public static final int NEVER_INDEX = 4;
/**
* Bookkeeping about when jobs last run. We keep our own record in heartbeat time,
@@ -268,22 +276,6 @@
// -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
- /**
- * This array essentially stores the state of mActiveServices array.
- * The ith index stores the job present on the ith JobServiceContext.
- * We manipulate this array until we arrive at what jobs should be running on
- * what JobServiceContext.
- */
- JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
- /**
- * Indicates whether we need to act on this jobContext id
- */
- boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
- /**
- * The uid whose jobs we would like to assign to a context.
- */
- int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
-
private class ConstantsObserver extends ContentObserver {
private ContentResolver mResolver;
@@ -308,6 +300,10 @@
try {
mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
Settings.Global.JOB_SCHEDULER_CONSTANTS));
+ for (int controller = 0; controller < mControllers.size(); controller++) {
+ final StateController sc = mControllers.get(controller);
+ sc.onConstantsUpdatedLocked();
+ }
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
@@ -315,8 +311,23 @@
}
}
- // Reset the heartbeat alarm based on the new heartbeat duration
- setNextHeartbeatAlarm();
+ if (mConstants.USE_HEARTBEATS) {
+ // Reset the heartbeat alarm based on the new heartbeat duration
+ setNextHeartbeatAlarm();
+ }
+ }
+ }
+
+ /**
+ * Thermal event received from Thermal Service
+ */
+ private final class ThermalStatusListener extends IThermalStatusListener.Stub {
+ @Override public void onStatusChange(int status) {
+ // Throttle for Temperature.THROTTLING_SEVERE and above
+ synchronized (mLock) {
+ mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
+ }
+ onControllerStateChanged();
}
}
@@ -352,6 +363,19 @@
private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
+ private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
+ private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
+ "qc_allowed_time_per_period_ms";
+ private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
+ "qc_in_quota_buffer_ms";
+ private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
+ "qc_window_size_active_ms";
+ private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
+ "qc_window_size_working_ms";
+ private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
+ "qc_window_size_frequent_ms";
+ private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
+ "qc_window_size_rare_ms";
private static final int DEFAULT_MIN_IDLE_COUNT = 1;
private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -377,6 +401,19 @@
private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
+ private static final boolean DEFAULT_USE_HEARTBEATS = true;
+ private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
+ 10 * 60 * 1000L; // 10 minutes
+ private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
+ 30 * 1000L; // 30 seconds
+ private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
+ 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
+ private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
+ 2 * 60 * 60 * 1000L; // 2 hours
+ private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
+ 8 * 60 * 60 * 1000L; // 8 hours
+ private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
+ 24 * 60 * 60 * 1000L; // 24 hours
/**
* Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -495,6 +532,54 @@
* we consider matching it against a metered network.
*/
public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
+ /**
+ * Whether to use heartbeats or rolling window for quota management. True will use
+ * heartbeats, false will use a rolling window.
+ */
+ public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
+
+ /** How much time each app will have to run jobs within their standby bucket window. */
+ public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
+ DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
+
+ /**
+ * How much time the package should have before transitioning from out-of-quota to in-quota.
+ * This should not affect processing if the package is already in-quota.
+ */
+ public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
+ DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+
+ /**
+ * The quota window size of the particular standby bucket. Apps in this standby bucket are
+ * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * WINDOW_SIZE_MS.
+ */
+ public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS;
+
+ /**
+ * The quota window size of the particular standby bucket. Apps in this standby bucket are
+ * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * WINDOW_SIZE_MS.
+ */
+ public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS;
+
+ /**
+ * The quota window size of the particular standby bucket. Apps in this standby bucket are
+ * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * WINDOW_SIZE_MS.
+ */
+ public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS;
+
+ /**
+ * The quota window size of the particular standby bucket. Apps in this standby bucket are
+ * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * WINDOW_SIZE_MS.
+ */
+ public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -567,6 +652,25 @@
DEFAULT_CONN_CONGESTION_DELAY_FRAC);
CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
DEFAULT_CONN_PREFETCH_RELAX_FRAC);
+ USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
+ QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
+ KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
+ DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
+ QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
+ KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
+ DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
+ QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
+ KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
+ QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
+ KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
+ QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
+ KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
+ QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
+ KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
+ DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
}
void dump(IndentingPrintWriter pw) {
@@ -600,6 +704,19 @@
pw.println('}');
pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
+ pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
+ QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
+ QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
pw.decreaseIndent();
}
@@ -629,6 +746,23 @@
}
proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
+ proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
+
+ final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
+ proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
+ QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
+ proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS,
+ QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
+ proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
+ proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
+ proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
+ proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS,
+ QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
+ proto.end(qcToken);
+
proto.end(token);
}
}
@@ -705,6 +839,15 @@
break;
}
}
+ if (DEBUG) {
+ Slog.d(TAG, "Something in " + pkgName
+ + " changed. Reevaluating controller states.");
+ }
+ synchronized (mLock) {
+ for (int c = mControllers.size() - 1; c >= 0; --c) {
+ mControllers.get(c).reevaluateStateLocked(pkgUid);
+ }
+ }
}
} else {
Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
@@ -908,6 +1051,8 @@
mJobPackageTracker.notePending(jobStatus);
addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
maybeRunPendingJobsLocked();
+ } else {
+ evaluateControllerStatesLocked(jobStatus);
}
}
return JobScheduler.RESULT_SUCCESS;
@@ -1047,8 +1192,10 @@
// with just the foreground priority. This means that persistent processes
// can never be the top app priority... that is fine.
mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
+ } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
} else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
+ mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
} else {
mUidPriorityOverride.delete(uid);
}
@@ -1138,6 +1285,8 @@
mConstantsObserver = new ConstantsObserver(mHandler);
mJobSchedulerStub = new JobSchedulerStub();
+ mConcurrencyManager = new JobConcurrencyManager(this);
+
// Set up the app standby bucketing tracker
mStandbyTracker = new StandbyTracker();
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
@@ -1162,6 +1311,7 @@
mControllers.add(new ContentObserverController(this));
mDeviceIdleJobsController = new DeviceIdleJobsController(this);
mControllers.add(mDeviceIdleJobsController);
+ mControllers.add(new QuotaController(this));
// If the job store determined that it can't yet reschedule persisted jobs,
// we need to start watching the clock.
@@ -1225,7 +1375,9 @@
mAppStateTracker = Preconditions.checkNotNull(
LocalServices.getService(AppStateTracker.class));
- setNextHeartbeatAlarm();
+ if (mConstants.USE_HEARTBEATS) {
+ setNextHeartbeatAlarm();
+ }
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter();
@@ -1249,6 +1401,16 @@
}
// Remove any jobs that are not associated with any of the current users.
cancelJobsForNonExistentUsers();
+ // Register thermal callback
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ if (mThermalService != null) {
+ try {
+ mThermalService.registerThermalStatusListener(new ThermalStatusListener());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal callback.", e);
+ }
+ }
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
// Let's go!
@@ -1672,14 +1834,26 @@
}
}
+ private boolean isJobThermalConstrainedLocked(JobStatus job) {
+ return mThermalConstraint && job.hasConnectivityConstraint()
+ && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
+ }
+
private void stopNonReadyActiveJobsLocked() {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext serviceContext = mActiveServices.get(i);
final JobStatus running = serviceContext.getRunningJobLocked();
- if (running != null && !running.isReady()) {
+ if (running == null) {
+ continue;
+ }
+ if (!running.isReady()) {
serviceContext.cancelExecutingJobLocked(
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
"cancelled due to unsatisfied constraints");
+ } else if (isJobThermalConstrainedLocked(running)) {
+ serviceContext.cancelExecutingJobLocked(
+ JobParameters.REASON_DEVICE_THERMAL,
+ "cancelled due to thermal condition");
}
}
}
@@ -1721,6 +1895,8 @@
newReadyJobs = new ArrayList<JobStatus>();
}
newReadyJobs.add(job);
+ } else {
+ evaluateControllerStatesLocked(job);
}
}
@@ -1794,6 +1970,8 @@
runnableJobs = new ArrayList<>();
}
runnableJobs.add(job);
+ } else {
+ evaluateControllerStatesLocked(job);
}
}
@@ -1869,6 +2047,9 @@
// Intentionally does not touch the alarm timing
void advanceHeartbeatLocked(long beatsElapsed) {
+ if (!mConstants.USE_HEARTBEATS) {
+ return;
+ }
mHeartbeat += beatsElapsed;
if (DEBUG_STANDBY) {
Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
@@ -1904,6 +2085,9 @@
void setNextHeartbeatAlarm() {
final long heartbeatLength;
synchronized (mLock) {
+ if (!mConstants.USE_HEARTBEATS) {
+ return;
+ }
heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
}
final long now = sElapsedRealtimeClock.millis();
@@ -1918,6 +2102,15 @@
HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
}
+ /** Returns true if both the calling and source users for the job are started. */
+ private boolean areUsersStartedLocked(final JobStatus job) {
+ boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
+ if (job.getUserId() == job.getSourceUserId()) {
+ return sourceStarted;
+ }
+ return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
+ }
+
/**
* Criteria for moving a job into the pending queue:
* - It's ready.
@@ -1961,6 +2154,10 @@
return false;
}
+ if (isJobThermalConstrainedLocked(job)) {
+ return false;
+ }
+
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
@@ -1976,48 +2173,51 @@
return false;
}
- // If the app is in a non-active standby bucket, make sure we've waited
- // an appropriate amount of time since the last invocation. During device-
- // wide parole, standby bucketing is ignored.
- //
- // Jobs in 'active' apps are not subject to standby, nor are jobs that are
- // specifically marked as exempt.
- if (DEBUG_STANDBY) {
- Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
- + " parole=" + mInParole + " active=" + job.uidActive
- + " exempt=" + job.getJob().isExemptedFromAppStandby());
- }
- if (!mInParole
- && !job.uidActive
- && !job.getJob().isExemptedFromAppStandby()) {
- final int bucket = job.getStandbyBucket();
+ if (mConstants.USE_HEARTBEATS) {
+ // If the app is in a non-active standby bucket, make sure we've waited
+ // an appropriate amount of time since the last invocation. During device-
+ // wide parole, standby bucketing is ignored.
+ //
+ // Jobs in 'active' apps are not subject to standby, nor are jobs that are
+ // specifically marked as exempt.
if (DEBUG_STANDBY) {
- Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat
- + " next=" + mNextBucketHeartbeat[bucket]);
+ Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ + " parole=" + mInParole + " active=" + job.uidActive
+ + " exempt=" + job.getJob().isExemptedFromAppStandby());
}
- if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
- // Only skip this job if the app is still waiting for the end of its nominal
- // bucket interval. Once it's waited that long, we let it go ahead and clear.
- // The final (NEVER) bucket is special; we never age those apps' jobs into
- // runnability.
- final long appLastRan = heartbeatWhenJobsLastRun(job);
- if (bucket >= mConstants.STANDBY_BEATS.length
- || (mHeartbeat > appLastRan
- && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
- // TODO: log/trace that we're deferring the job due to bucketing if we hit this
- if (job.getWhenStandbyDeferred() == 0) {
- if (DEBUG_STANDBY) {
- Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
- + (appLastRan + mConstants.STANDBY_BEATS[bucket])
- + " for " + job);
+ if (!mInParole
+ && !job.uidActive
+ && !job.getJob().isExemptedFromAppStandby()) {
+ final int bucket = job.getStandbyBucket();
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat
+ + " next=" + mNextBucketHeartbeat[bucket]);
+ }
+ if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
+ // Only skip this job if the app is still waiting for the end of its nominal
+ // bucket interval. Once it's waited that long, we let it go ahead and clear.
+ // The final (NEVER) bucket is special; we never age those apps' jobs into
+ // runnability.
+ final long appLastRan = heartbeatWhenJobsLastRun(job);
+ if (bucket >= mConstants.STANDBY_BEATS.length
+ || (mHeartbeat > appLastRan
+ && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
+ // TODO: log/trace that we're deferring the job due to bucketing if we
+ // hit this
+ if (job.getWhenStandbyDeferred() == 0) {
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
+ + (appLastRan + mConstants.STANDBY_BEATS[bucket])
+ + " for " + job);
+ }
+ job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
}
- job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
- }
- return false;
- } else {
- if (DEBUG_STANDBY) {
- Slog.v(TAG, "Bucket deferred job aged into runnability at "
- + mHeartbeat + " : " + job);
+ return false;
+ } else {
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, "Bucket deferred job aged into runnability at "
+ + mHeartbeat + " : " + job);
+ }
}
}
}
@@ -2043,6 +2243,61 @@
return componentPresent;
}
+ private void evaluateControllerStatesLocked(final JobStatus job) {
+ for (int c = mControllers.size() - 1; c >= 0; --c) {
+ final StateController sc = mControllers.get(c);
+ sc.evaluateStateLocked(job);
+ }
+ }
+
+ /**
+ * Returns true if non-job constraint components are in place -- if job.isReady() returns true
+ * and this method returns true, then the job is ready to be executed.
+ */
+ public boolean areComponentsInPlaceLocked(JobStatus job) {
+ // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
+ // conditions.
+
+ final boolean jobExists = mJobs.containsJob(job);
+ final boolean userStarted = areUsersStartedLocked(job);
+
+ if (DEBUG) {
+ Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
+ + " exists=" + jobExists + " userStarted=" + userStarted);
+ }
+
+ // These are also fairly cheap to check, though they typically will not
+ // be conditions we fail.
+ if (!jobExists || !userStarted) {
+ return false;
+ }
+
+ // Job pending/active doesn't affect the readiness of a job.
+
+ // Skipping the hearbeat check as this will only come into play when using the rolling
+ // window quota management system.
+
+ // The expensive check last: validate that the defined package+service is
+ // still present & viable.
+ final boolean componentPresent;
+ try {
+ // TODO: cache result until we're notified that something in the package changed.
+ componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
+ job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ job.getUserId()) != null);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
+ + " componentPresent=" + componentPresent);
+ }
+
+ // Everything else checked out so far, so this is the final yes/no check
+ return componentPresent;
+ }
+
/**
* Reconcile jobs in the pending queue against available execution contexts.
* A controller can force a job into the pending queue even if it's already running, but
@@ -2052,7 +2307,7 @@
if (DEBUG) {
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
}
- assignJobsToContextsLocked();
+ mConcurrencyManager.assignJobsToContextsLocked();
reportActiveLocked();
}
@@ -2068,9 +2323,9 @@
return curPriority;
}
- private int evaluateJobPriorityLocked(JobStatus job) {
+ int evaluateJobPriorityLocked(JobStatus job) {
int priority = job.getPriority();
- if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
+ if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
return adjustJobPriority(priority, job);
}
int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
@@ -2080,161 +2335,6 @@
return adjustJobPriority(priority, job);
}
- /**
- * Takes jobs from pending queue and runs them on available contexts.
- * If no contexts are available, preempts lower priority jobs to
- * run higher priority ones.
- * Lock on mJobs before calling this function.
- */
- private void assignJobsToContextsLocked() {
- if (DEBUG) {
- Slog.d(TAG, printPendingQueue());
- }
-
- int memLevel;
- try {
- memLevel = ActivityManager.getService().getMemoryTrimLevel();
- } catch (RemoteException e) {
- memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
- }
- switch (memLevel) {
- case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
- mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
- mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
- break;
- default:
- mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
- break;
- }
-
- JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
- boolean[] act = mTmpAssignAct;
- int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
- int numActive = 0;
- int numForeground = 0;
- for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
- final JobServiceContext js = mActiveServices.get(i);
- final JobStatus status = js.getRunningJobLocked();
- if ((contextIdToJobMap[i] = status) != null) {
- numActive++;
- if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
- }
- }
- act[i] = false;
- preferredUidForContext[i] = js.getPreferredUid();
- }
- if (DEBUG) {
- Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
- }
- for (int i=0; i<mPendingJobs.size(); i++) {
- JobStatus nextPending = mPendingJobs.get(i);
-
- // If job is already running, go to next job.
- int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
- if (jobRunningContext != -1) {
- continue;
- }
-
- final int priority = evaluateJobPriorityLocked(nextPending);
- nextPending.lastEvaluatedPriority = priority;
-
- // Find a context for nextPending. The context should be available OR
- // it should have lowest priority among all running jobs
- // (sharing the same Uid as nextPending)
- int minPriority = Integer.MAX_VALUE;
- int minPriorityContextId = -1;
- for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
- JobStatus job = contextIdToJobMap[j];
- int preferredUid = preferredUidForContext[j];
- if (job == null) {
- if ((numActive < mMaxActiveJobs ||
- (priority >= JobInfo.PRIORITY_TOP_APP &&
- numForeground < mConstants.FG_JOB_COUNT)) &&
- (preferredUid == nextPending.getUid() ||
- preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
- // This slot is free, and we haven't yet hit the limit on
- // concurrent jobs... we can just throw the job in to here.
- minPriorityContextId = j;
- break;
- }
- // No job on this context, but nextPending can't run here because
- // the context has a preferred Uid or we have reached the limit on
- // concurrent jobs.
- continue;
- }
- if (job.getUid() != nextPending.getUid()) {
- continue;
- }
- if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
- continue;
- }
- if (minPriority > nextPending.lastEvaluatedPriority) {
- minPriority = nextPending.lastEvaluatedPriority;
- minPriorityContextId = j;
- }
- }
- if (minPriorityContextId != -1) {
- contextIdToJobMap[minPriorityContextId] = nextPending;
- act[minPriorityContextId] = true;
- numActive++;
- if (priority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
- }
- }
- }
- if (DEBUG) {
- Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
- }
- mJobPackageTracker.noteConcurrency(numActive, numForeground);
- for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
- boolean preservePreferredUid = false;
- if (act[i]) {
- JobStatus js = mActiveServices.get(i).getRunningJobLocked();
- if (js != null) {
- if (DEBUG) {
- Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
- }
- // preferredUid will be set to uid of currently running job.
- mActiveServices.get(i).preemptExecutingJobLocked();
- preservePreferredUid = true;
- } else {
- final JobStatus pendingJob = contextIdToJobMap[i];
- if (DEBUG) {
- Slog.d(TAG, "About to run job on context "
- + String.valueOf(i) + ", job: " + pendingJob);
- }
- for (int ic=0; ic<mControllers.size(); ic++) {
- mControllers.get(ic).prepareForExecutionLocked(pendingJob);
- }
- if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
- Slog.d(TAG, "Error executing " + pendingJob);
- }
- if (mPendingJobs.remove(pendingJob)) {
- mJobPackageTracker.noteNonpending(pendingJob);
- }
- }
- }
- if (!preservePreferredUid) {
- mActiveServices.get(i).clearPreferredUid();
- }
- }
- }
-
- int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
- for (int i=0; i<map.length; i++) {
- if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
- return i;
- }
- }
- return -1;
- }
-
final class LocalService implements JobSchedulerInternal {
/**
@@ -2364,32 +2464,7 @@
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
boolean idle, int bucket, int reason) {
- final int uid = mLocalPM.getPackageUid(packageName,
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- if (uid < 0) {
- if (DEBUG_STANDBY) {
- Slog.i(TAG, "App idle state change for unknown app "
- + packageName + "/" + userId);
- }
- return;
- }
-
- final int bucketIndex = standbyBucketToBucketIndex(bucket);
- // update job bookkeeping out of band
- BackgroundThread.getHandler().post(() -> {
- if (DEBUG_STANDBY) {
- Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
- }
- synchronized (mLock) {
- mJobs.forEachJobForSourceUid(uid, job -> {
- // double-check uid vs package name to disambiguate shared uids
- if (packageName.equals(job.getSourcePackageName())) {
- job.setStandbyBucket(bucketIndex);
- }
- });
- onControllerStateChanged();
- }
- });
+ // QuotaController handles this now.
}
@Override
@@ -3034,31 +3109,6 @@
getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
}
- private String printContextIdToJobMap(JobStatus[] map, String initial) {
- StringBuilder s = new StringBuilder(initial + ": ");
- for (int i=0; i<map.length; i++) {
- s.append("(")
- .append(map[i] == null? -1: map[i].getJobId())
- .append(map[i] == null? -1: map[i].getUid())
- .append(")" );
- }
- return s.toString();
- }
-
- private String printPendingQueue() {
- StringBuilder s = new StringBuilder("Pending queue: ");
- Iterator<JobStatus> it = mPendingJobs.iterator();
- while (it.hasNext()) {
- JobStatus js = it.next();
- s.append("(")
- .append(js.getJob().getId())
- .append(", ")
- .append(js.getUid())
- .append(") ");
- }
- return s.toString();
- }
-
static void dumpHelp(PrintWriter pw) {
pw.println("Job Scheduler (jobscheduler) dump options:");
pw.println(" [-h] [package] ...");
@@ -3112,6 +3162,9 @@
pw.print(" In parole?: ");
pw.print(mInParole);
pw.println();
+ pw.print(" In thermal throttling?: ");
+ pw.print(mThermalConstraint);
+ pw.println();
pw.println();
pw.println("Started users: " + Arrays.toString(mStartedUsers));
@@ -3209,9 +3262,9 @@
pw.println(job.toShortString());
job.dump(pw, " ", false, nowElapsed);
int priority = evaluateJobPriorityLocked(job);
- if (priority != JobInfo.PRIORITY_DEFAULT) {
- pw.print(" Evaluated priority: "); pw.println(priority);
- }
+ pw.print(" Evaluated priority: ");
+ pw.println(JobInfo.getPriorityString(priority));
+
pw.print(" Tag: "); pw.println(job.getTag());
pw.print(" Enq: ");
TimeUtils.formatDuration(job.madePending - nowUptime, pw);
@@ -3242,9 +3295,9 @@
pw.println();
job.dump(pw, " ", false, nowElapsed);
int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
- if (priority != JobInfo.PRIORITY_DEFAULT) {
- pw.print(" Evaluated priority: "); pw.println(priority);
- }
+ pw.print(" Evaluated priority: ");
+ pw.println(JobInfo.getPriorityString(priority));
+
pw.print(" Active at ");
TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
pw.print(", pending for ");
@@ -3287,6 +3340,7 @@
proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
+ proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
for (int u : mStartedUsers) {
proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
@@ -3362,10 +3416,7 @@
job.writeToShortProto(proto, PendingJob.INFO);
job.dump(proto, PendingJob.DUMP, false, nowElapsed);
- int priority = evaluateJobPriorityLocked(job);
- if (priority != JobInfo.PRIORITY_DEFAULT) {
- proto.write(PendingJob.EVALUATED_PRIORITY, priority);
- }
+ proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
proto.end(pjToken);
@@ -3397,10 +3448,8 @@
job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
- int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
- if (priority != JobInfo.PRIORITY_DEFAULT) {
- proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
- }
+ proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
+ evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
nowUptime - job.madeActive);
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 6989c33..8f104e4 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -41,10 +41,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerService.Constants;
import com.android.server.job.JobServiceContext;
import com.android.server.job.StateControllerProto;
+import com.android.server.net.NetworkPolicyManagerInternal;
import java.util.Objects;
import java.util.function.Predicate;
@@ -66,16 +68,29 @@
private final ConnectivityManager mConnManager;
private final NetworkPolicyManager mNetPolicyManager;
+ private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
/** List of tracked jobs keyed by source UID. */
@GuardedBy("mLock")
private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
+ /**
+ * Keep track of all the UID's jobs that the controller has requested that NetworkPolicyManager
+ * grant an exception to in the app standby chain.
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>();
+
+ /** List of currently available networks. */
+ @GuardedBy("mLock")
+ private final ArraySet<Network> mAvailableNetworks = new ArraySet<>();
+
public ConnectivityController(JobSchedulerService service) {
super(service);
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
+ mNetPolicyManagerInternal = LocalServices.getService(NetworkPolicyManagerInternal.class);
// We're interested in all network changes; internally we match these
// network changes against the active network for each UID with jobs.
@@ -109,9 +124,178 @@
if (jobs != null) {
jobs.remove(jobStatus);
}
+ maybeRevokeStandbyExceptionLocked(jobStatus);
}
}
+ @GuardedBy("mLock")
+ @Override
+ public void onConstantsUpdatedLocked() {
+ if (mConstants.USE_HEARTBEATS) {
+ // App idle exceptions are only requested for the rolling quota system.
+ if (DEBUG) Slog.i(TAG, "Revoking all standby exceptions");
+ for (int i = 0; i < mRequestedWhitelistJobs.size(); ++i) {
+ int uid = mRequestedWhitelistJobs.keyAt(i);
+ mNetPolicyManagerInternal.setAppIdleWhitelist(uid, false);
+ }
+ mRequestedWhitelistJobs.clear();
+ }
+ }
+
+ /**
+ * Returns true if the job's requested network is available. This DOES NOT necesarilly mean
+ * that the UID has been granted access to the network.
+ */
+ public boolean isNetworkAvailable(JobStatus job) {
+ synchronized (mLock) {
+ for (int i = 0; i < mAvailableNetworks.size(); ++i) {
+ final Network network = mAvailableNetworks.valueAt(i);
+ final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(
+ network);
+ final boolean satisfied = isSatisfied(job, network, capabilities, mConstants);
+ if (DEBUG) {
+ Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network
+ + " and capabilities " + capabilities + ". Satisfied=" + satisfied);
+ }
+ if (satisfied) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Request that NetworkPolicyManager grant an exception to the uid from its standby policy
+ * chain.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void requestStandbyExceptionLocked(JobStatus job) {
+ final int uid = job.getSourceUid();
+ // Need to call this before adding the job.
+ final boolean isExceptionRequested = isStandbyExceptionRequestedLocked(uid);
+ ArraySet<JobStatus> jobs = mRequestedWhitelistJobs.get(uid);
+ if (jobs == null) {
+ jobs = new ArraySet<JobStatus>();
+ mRequestedWhitelistJobs.put(uid, jobs);
+ }
+ if (!jobs.add(job) || isExceptionRequested) {
+ if (DEBUG) {
+ Slog.i(TAG, "requestStandbyExceptionLocked found exception already requested.");
+ }
+ return;
+ }
+ if (DEBUG) Slog.i(TAG, "Requesting standby exception for UID: " + uid);
+ mNetPolicyManagerInternal.setAppIdleWhitelist(uid, true);
+ }
+
+ /** Returns whether a standby exception has been requested for the UID. */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ boolean isStandbyExceptionRequestedLocked(final int uid) {
+ ArraySet jobs = mRequestedWhitelistJobs.get(uid);
+ return jobs != null && jobs.size() > 0;
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ boolean wouldBeReadyWithConnectivityLocked(JobStatus jobStatus) {
+ final boolean networkAvailable = isNetworkAvailable(jobStatus);
+ if (DEBUG) {
+ Slog.v(TAG, "wouldBeReadyWithConnectivityLocked: " + jobStatus.toShortString()
+ + " networkAvailable=" + networkAvailable);
+ }
+ // If the network isn't available, then requesting an exception won't help.
+
+ return networkAvailable && wouldBeReadyWithConstraintLocked(jobStatus,
+ JobStatus.CONSTRAINT_CONNECTIVITY);
+ }
+
+ /**
+ * Tell NetworkPolicyManager not to block a UID's network connection if that's the only
+ * thing stopping a job from running.
+ */
+ @GuardedBy("mLock")
+ @Override
+ public void evaluateStateLocked(JobStatus jobStatus) {
+ if (mConstants.USE_HEARTBEATS) {
+ // This should only be used for the rolling quota system.
+ return;
+ }
+
+ if (!jobStatus.hasConnectivityConstraint()) {
+ return;
+ }
+
+ // Always check the full job readiness stat in case the component has been disabled.
+ if (wouldBeReadyWithConnectivityLocked(jobStatus)) {
+ if (DEBUG) {
+ Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would be ready.");
+ }
+ requestStandbyExceptionLocked(jobStatus);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would not be ready.");
+ }
+ maybeRevokeStandbyExceptionLocked(jobStatus);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Override
+ public void reevaluateStateLocked(final int uid) {
+ if (mConstants.USE_HEARTBEATS) {
+ return;
+ }
+ // Check if we still need a connectivity exception in case the JobService was disabled.
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(uid);
+ if (jobs == null) {
+ return;
+ }
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ evaluateStateLocked(jobs.valueAt(i));
+ }
+ }
+
+ /** Cancel the requested standby exception if none of the jobs would be ready to run anyway. */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void maybeRevokeStandbyExceptionLocked(final JobStatus job) {
+ final int uid = job.getSourceUid();
+ if (!isStandbyExceptionRequestedLocked(uid)) {
+ return;
+ }
+ ArraySet<JobStatus> jobs = mRequestedWhitelistJobs.get(uid);
+ if (jobs == null) {
+ Slog.wtf(TAG,
+ "maybeRevokeStandbyExceptionLocked found null jobs array even though a "
+ + "standby exception has been requested.");
+ return;
+ }
+ if (!jobs.remove(job) || jobs.size() > 0) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "maybeRevokeStandbyExceptionLocked not revoking because there are still "
+ + jobs.size() + " jobs left.");
+ }
+ return;
+ }
+ // No more jobs that need an exception.
+ revokeStandbyExceptionLocked(uid);
+ }
+
+ /**
+ * Tell NetworkPolicyManager to revoke any exception it granted from its standby policy chain
+ * for the uid.
+ */
+ @GuardedBy("mLock")
+ private void revokeStandbyExceptionLocked(final int uid) {
+ if (DEBUG) Slog.i(TAG, "Revoking standby exception for UID: " + uid);
+ mNetPolicyManagerInternal.setAppIdleWhitelist(uid, false);
+ mRequestedWhitelistJobs.remove(uid);
+ }
+
/**
* Test to see if running the given job on the given network is insane.
* <p>
@@ -326,6 +510,14 @@
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
+ public void onAvailable(Network network) {
+ if (DEBUG) Slog.v(TAG, "onAvailable: " + network);
+ synchronized (mLock) {
+ mAvailableNetworks.add(network);
+ }
+ }
+
+ @Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
if (DEBUG) {
Slog.v(TAG, "onCapabilitiesChanged: " + network);
@@ -338,6 +530,9 @@
if (DEBUG) {
Slog.v(TAG, "onLost: " + network);
}
+ synchronized (mLock) {
+ mAvailableNetworks.remove(network);
+ }
updateTrackedJobs(-1, network);
}
};
@@ -356,6 +551,27 @@
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
+ if (mRequestedWhitelistJobs.size() > 0) {
+ pw.print("Requested standby exceptions:");
+ for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) {
+ pw.print(" ");
+ pw.print(mRequestedWhitelistJobs.keyAt(i));
+ pw.print(" (");
+ pw.print(mRequestedWhitelistJobs.valueAt(i).size());
+ pw.print(" jobs)");
+ }
+ pw.println();
+ }
+ if (mAvailableNetworks.size() > 0) {
+ pw.println("Available networks:");
+ pw.increaseIndent();
+ for (int i = 0; i < mAvailableNetworks.size(); i++) {
+ pw.println(mAvailableNetworks.valueAt(i));
+ }
+ pw.decreaseIndent();
+ } else {
+ pw.println("No available networks");
+ }
for (int i = 0; i < mTrackedJobs.size(); i++) {
final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
for (int j = 0; j < jobs.size(); j++) {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 35fc29e..82bfa51 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -77,6 +77,7 @@
static final int CONSTRAINT_CONNECTIVITY = 1<<28;
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25;
+ static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
// Soft override: ignore constraints like time that don't affect API availability
@@ -192,6 +193,10 @@
* Flag for {@link #trackingControllers}: the time controller is currently tracking this job.
*/
public static final int TRACKING_TIME = 1<<5;
+ /**
+ * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job.
+ */
+ public static final int TRACKING_QUOTA = 1 << 6;
/**
* Bit mask of controllers that are currently tracking the job.
@@ -291,6 +296,9 @@
*/
private boolean mReadyNotRestrictedInBg;
+ /** The job is within its quota based on its standby bucket. */
+ private boolean mReadyWithinQuota;
+
/** Provide a handle to the service that this job will be run on. */
public int getServiceToken() {
return callingUid;
@@ -675,7 +683,6 @@
return baseHeartbeat;
}
- // Called only by the standby monitoring code
public void setStandbyBucket(int newBucket) {
standbyBucket = newBucket;
}
@@ -876,22 +883,27 @@
mPersistedUtcTimes = null;
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setChargingConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setBatteryNotLowConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setStorageNotLowConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setTimingDelayConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setDeadlineConstraintSatisfied(boolean state) {
if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
// The constraint was changed. Update the ready flag.
@@ -901,18 +913,22 @@
return false;
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setIdleConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_IDLE, state);
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setConnectivityConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setContentTriggerConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
dozeWhitelisted = whitelisted;
if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
@@ -923,6 +939,7 @@
return false;
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
// The constraint was changed. Update the ready flag.
@@ -932,6 +949,17 @@
return false;
}
+ /** @return true if the constraint was changed, false otherwise. */
+ boolean setQuotaConstraintSatisfied(boolean state) {
+ if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyWithinQuota = state;
+ return true;
+ }
+ return false;
+ }
+
+ /** @return true if the state was changed, false otherwise. */
boolean setUidActive(final boolean newActiveState) {
if (newActiveState != uidActive) {
uidActive = newActiveState;
@@ -940,6 +968,7 @@
return false; /* unchanged */
}
+ /** @return true if the constraint was changed, false otherwise. */
boolean setConstraintSatisfied(int constraint, boolean state) {
boolean old = (satisfiedConstraints&constraint) != 0;
if (old == state) {
@@ -978,13 +1007,29 @@
* @return Whether or not this job is ready to run, based on its requirements.
*/
public boolean isReady() {
- // Deadline constraint trumps other constraints (except for periodic jobs where deadline
- // is an implementation detail. A periodic job should only run if its constraints are
- // satisfied).
+ return isReady(mSatisfiedConstraintsOfInterest);
+ }
+
+ /**
+ * @return Whether or not this job would be ready to run if it had the specified constraint
+ * granted, based on its requirements.
+ */
+ public boolean wouldBeReadyWithConstraint(int constraint) {
+ return isReady(mSatisfiedConstraintsOfInterest | constraint);
+ }
+
+ private boolean isReady(int satisfiedConstraints) {
+ // Quota constraints trumps all other constraints.
+ if (!mReadyWithinQuota) {
+ return false;
+ }
+ // Deadline constraint trumps other constraints besides quota (except for periodic jobs
+ // where deadline is an implementation detail. A periodic job should only run if its
+ // constraints are satisfied).
// DeviceNotDozing implicit constraint must be satisfied
// NotRestrictedInBackground implicit constraint must be satisfied
return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
- || isConstraintsSatisfied());
+ || isConstraintsSatisfied(satisfiedConstraints));
}
static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
@@ -1000,12 +1045,16 @@
* @return Whether the constraints set on this job are satisfied.
*/
public boolean isConstraintsSatisfied() {
+ return isConstraintsSatisfied(mSatisfiedConstraintsOfInterest);
+ }
+
+ private boolean isConstraintsSatisfied(int satisfiedConstraints) {
if (overrideState == OVERRIDE_FULL) {
// force override: the job is always runnable
return true;
}
- int sat = mSatisfiedConstraintsOfInterest;
+ int sat = satisfiedConstraints;
if (overrideState == OVERRIDE_SOFT) {
// override: pretend all 'soft' requirements are satisfied
sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
@@ -1169,6 +1218,9 @@
if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
pw.print(" BACKGROUND_NOT_RESTRICTED");
}
+ if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
+ pw.print(" WITHIN_QUOTA");
+ }
if (constraints != 0) {
pw.print(" [0x");
pw.print(Integer.toHexString(constraints));
@@ -1205,6 +1257,9 @@
if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING);
}
+ if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
+ proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_WITHIN_QUOTA);
+ }
}
private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
@@ -1237,6 +1292,13 @@
* Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
*/
String getBucketName() {
+ return bucketName(standbyBucket);
+ }
+
+ /**
+ * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
+ */
+ static String bucketName(int standbyBucket) {
switch (standbyBucket) {
case 0: return "ACTIVE";
case 1: return "WORKING_SET";
@@ -1275,7 +1337,8 @@
pw.print(prefix); pw.println(" PERSISTED");
}
if (job.getPriority() != 0) {
- pw.print(prefix); pw.print(" Priority: "); pw.println(job.getPriority());
+ pw.print(prefix); pw.print(" Priority: ");
+ pw.println(JobInfo.getPriorityString(job.getPriority()));
}
if (job.getFlags() != 0) {
pw.print(prefix); pw.print(" Flags: ");
@@ -1367,7 +1430,8 @@
dumpConstraints(pw, satisfiedConstraints);
pw.println();
pw.print(prefix); pw.print("Unsatisfied constraints:");
- dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints));
+ dumpConstraints(pw,
+ ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
pw.println();
if (dozeWhitelisted) {
pw.print(prefix); pw.println("Doze whitelisted: true");
@@ -1375,6 +1439,9 @@
if (uidActive) {
pw.print(prefix); pw.println("Uid: active");
}
+ if (job.isExemptedFromAppStandby()) {
+ pw.print(prefix); pw.println("Is exempted from app standby");
+ }
}
if (trackingControllers != 0) {
pw.print(prefix); pw.print("Tracking:");
@@ -1384,6 +1451,7 @@
if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE");
if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE");
if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
+ if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA");
pw.println();
}
@@ -1546,8 +1614,11 @@
if (full) {
dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
- (requiredConstraints & ~satisfiedConstraints));
+ ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, dozeWhitelisted);
+ proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive);
+ proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY,
+ job.isExemptedFromAppStandby());
}
// Tracking controllers
@@ -1575,6 +1646,10 @@
proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
JobStatusDumpProto.TRACKING_TIME);
}
+ if ((trackingControllers & TRACKING_QUOTA) != 0) {
+ proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+ JobStatusDumpProto.TRACKING_QUOTA);
+ }
// Implicit constraints
final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS);
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
new file mode 100644
index 0000000..660c238
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -0,0 +1,1312 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AlarmManager;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateControllerProto;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Controller that tracks whether a package has exceeded its standby bucket quota.
+ *
+ * Each job in each bucket is given 10 minutes to run within its respective time window. Active
+ * jobs can run indefinitely, working set jobs can run for 10 minutes within a 2 hour window,
+ * frequent jobs get to run 10 minutes in an 8 hour window, and rare jobs get to run 10 minutes in
+ * a 24 hour window. The windows are rolling, so as soon as a job would have some quota based on its
+ * bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately
+ * applied to it.
+ *
+ * Test: atest com.android.server.job.controllers.QuotaControllerTest
+ */
+public final class QuotaController extends StateController {
+ private static final String TAG = "JobScheduler.Quota";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final long MINUTE_IN_MILLIS = 60 * 1000L;
+
+ private static final String ALARM_TAG_CLEANUP = "*job.cleanup*";
+ private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*";
+
+ /**
+ * A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object
+ * associations.
+ */
+ private static class UserPackageMap<T> {
+ private final SparseArray<ArrayMap<String, T>> mData = new SparseArray<>();
+
+ public void add(int userId, @NonNull String packageName, @Nullable T obj) {
+ ArrayMap<String, T> data = mData.get(userId);
+ if (data == null) {
+ data = new ArrayMap<String, T>();
+ mData.put(userId, data);
+ }
+ data.put(packageName, obj);
+ }
+
+ @Nullable
+ public T get(int userId, @NonNull String packageName) {
+ ArrayMap<String, T> data = mData.get(userId);
+ if (data != null) {
+ return data.get(packageName);
+ }
+ return null;
+ }
+
+ /** Returns the userId at the given index. */
+ public int keyAt(int index) {
+ return mData.keyAt(index);
+ }
+
+ /** Returns the package name at the given index. */
+ @NonNull
+ public String keyAt(int userIndex, int packageIndex) {
+ return mData.valueAt(userIndex).keyAt(packageIndex);
+ }
+
+ /** Returns the size of the outer (userId) array. */
+ public int numUsers() {
+ return mData.size();
+ }
+
+ public int numPackagesForUser(int userId) {
+ ArrayMap<String, T> data = mData.get(userId);
+ return data == null ? 0 : data.size();
+ }
+
+ /** Returns the value T at the given user and index. */
+ @Nullable
+ public T valueAt(int userIndex, int packageIndex) {
+ return mData.valueAt(userIndex).valueAt(packageIndex);
+ }
+
+ public void forEach(Consumer<T> consumer) {
+ for (int i = numUsers() - 1; i >= 0; --i) {
+ ArrayMap<String, T> data = mData.valueAt(i);
+ for (int j = data.size() - 1; j >= 0; --j) {
+ consumer.accept(data.valueAt(j));
+ }
+ }
+ }
+ }
+
+ /**
+ * Standardize the output of userId-packageName combo.
+ */
+ private static String string(int userId, String packageName) {
+ return "<" + userId + ">" + packageName;
+ }
+
+ private static final class Package {
+ public final String packageName;
+ public final int userId;
+
+ Package(int userId, String packageName) {
+ this.userId = userId;
+ this.packageName = packageName;
+ }
+
+ @Override
+ public String toString() {
+ return string(userId, packageName);
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(StateControllerProto.QuotaController.Package.USER_ID, userId);
+ proto.write(StateControllerProto.QuotaController.Package.NAME, packageName);
+
+ proto.end(token);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Package) {
+ Package other = (Package) obj;
+ return userId == other.userId && Objects.equals(packageName, other.packageName);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return packageName.hashCode() + userId;
+ }
+ }
+
+ /** List of all tracked jobs keyed by source package-userId combo. */
+ private final UserPackageMap<ArraySet<JobStatus>> mTrackedJobs = new UserPackageMap<>();
+
+ /** Timer for each package-userId combo. */
+ private final UserPackageMap<Timer> mPkgTimers = new UserPackageMap<>();
+
+ /** List of all timing sessions for a package-userId combo, in chronological order. */
+ private final UserPackageMap<List<TimingSession>> mTimingSessions = new UserPackageMap<>();
+
+ /**
+ * List of alarm listeners for each package that listen for when each package comes back within
+ * quota.
+ */
+ private final UserPackageMap<QcAlarmListener> mInQuotaAlarmListeners = new UserPackageMap<>();
+
+ private final AlarmManager mAlarmManager;
+ private final ChargingTracker mChargeTracker;
+ private final Handler mHandler;
+
+ private volatile boolean mInParole;
+
+ /**
+ * If the QuotaController should throttle apps based on their standby bucket and job activity.
+ * If false, all jobs will have their CONSTRAINT_WITHIN_QUOTA bit set to true immediately and
+ * indefinitely.
+ */
+ private boolean mShouldThrottle;
+
+ /** How much time each app will have to run jobs within their standby bucket window. */
+ private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
+
+ /**
+ * How much time the package should have before transitioning from out-of-quota to in-quota.
+ * This should not affect processing if the package is already in-quota.
+ */
+ private long mQuotaBufferMs = 30 * 1000L; // 30 seconds
+
+ private long mNextCleanupTimeElapsed = 0;
+ private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
+ new AlarmManager.OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ mHandler.obtainMessage(MSG_CLEAN_UP_SESSIONS).sendToTarget();
+ }
+ };
+
+ /**
+ * The rolling window size for each standby bucket. Within each window, an app will have 10
+ * minutes to run its jobs.
+ */
+ private final long[] mBucketPeriodsMs = new long[] {
+ 10 * MINUTE_IN_MILLIS, // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
+ 2 * 60 * MINUTE_IN_MILLIS, // 2 hours for WORKING
+ 8 * 60 * MINUTE_IN_MILLIS, // 8 hours for FREQUENT
+ 24 * 60 * MINUTE_IN_MILLIS // 24 hours for RARE
+ };
+
+ /** The maximum period any bucket can have. */
+ private static final long MAX_PERIOD_MS = 24 * 60 * MINUTE_IN_MILLIS;
+
+ /** A package has reached its quota. The message should contain a {@link Package} object. */
+ private static final int MSG_REACHED_QUOTA = 0;
+ /** Drop any old timing sessions. */
+ private static final int MSG_CLEAN_UP_SESSIONS = 1;
+ /** Check if a package is now within its quota. */
+ private static final int MSG_CHECK_PACKAGE = 2;
+
+ public QuotaController(JobSchedulerService service) {
+ super(service);
+ mHandler = new QcHandler(mContext.getMainLooper());
+ mChargeTracker = new ChargingTracker();
+ mChargeTracker.startTracking();
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+
+ // Set up the app standby bucketing tracker
+ UsageStatsManagerInternal usageStats = LocalServices.getService(
+ UsageStatsManagerInternal.class);
+ usageStats.addAppIdleStateChangeListener(new StandbyTracker());
+
+ onConstantsUpdatedLocked();
+ }
+
+ @Override
+ public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+ // Still need to track jobs even if mShouldThrottle is false in case it's set to true at
+ // some point.
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
+ jobStatus.getSourcePackageName());
+ if (jobs == null) {
+ jobs = new ArraySet<>();
+ mTrackedJobs.add(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), jobs);
+ }
+ jobs.add(jobStatus);
+ jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
+ jobStatus.setQuotaConstraintSatisfied(!mShouldThrottle || isWithinQuotaLocked(jobStatus));
+ }
+
+ @Override
+ public void prepareForExecutionLocked(JobStatus jobStatus) {
+ if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+ final int userId = jobStatus.getSourceUserId();
+ final String packageName = jobStatus.getSourcePackageName();
+ Timer timer = mPkgTimers.get(userId, packageName);
+ if (timer == null) {
+ timer = new Timer(userId, packageName);
+ mPkgTimers.add(userId, packageName, timer);
+ }
+ timer.startTrackingJob(jobStatus);
+ }
+
+ @Override
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+ if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
+ Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(),
+ jobStatus.getSourcePackageName());
+ if (timer != null) {
+ timer.stopTrackingJob(jobStatus);
+ }
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
+ jobStatus.getSourcePackageName());
+ if (jobs != null) {
+ jobs.remove(jobStatus);
+ }
+ }
+ }
+
+ @Override
+ public void onConstantsUpdatedLocked() {
+ boolean changed = false;
+ if (mShouldThrottle == mConstants.USE_HEARTBEATS) {
+ mShouldThrottle = !mConstants.USE_HEARTBEATS;
+ changed = true;
+ }
+ long newAllowedTimeMs = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS));
+ if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
+ mAllowedTimePerPeriodMs = newAllowedTimeMs;
+ changed = true;
+ }
+ long newQuotaBufferMs = Math.max(0,
+ Math.min(5 * MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS));
+ if (mQuotaBufferMs != newQuotaBufferMs) {
+ mQuotaBufferMs = newQuotaBufferMs;
+ changed = true;
+ }
+ long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS));
+ if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
+ mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
+ changed = true;
+ }
+ long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS));
+ if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
+ mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
+ changed = true;
+ }
+ long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS));
+ if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
+ mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
+ changed = true;
+ }
+ long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS));
+ if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
+ mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
+ changed = true;
+ }
+
+ if (changed) {
+ // Update job bookkeeping out of band.
+ BackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ maybeUpdateAllConstraintsLocked();
+ }
+ });
+ }
+ }
+
+ /**
+ * Returns an appropriate standby bucket for the job, taking into account any standby
+ * exemptions.
+ */
+ private int getEffectiveStandbyBucket(@NonNull final JobStatus jobStatus) {
+ if (jobStatus.uidActive || jobStatus.getJob().isExemptedFromAppStandby()) {
+ // Treat these cases as if they're in the ACTIVE bucket so that they get throttled
+ // like other ACTIVE apps.
+ return ACTIVE_INDEX;
+ }
+ return jobStatus.getStandbyBucket();
+ }
+
+ private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
+ final int standbyBucket = getEffectiveStandbyBucket(jobStatus);
+ // Jobs for the active app should always be able to run.
+ return jobStatus.uidActive || isWithinQuotaLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
+ }
+
+ private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
+ final int standbyBucket) {
+ if (standbyBucket == NEVER_INDEX) return false;
+ if (standbyBucket == ACTIVE_INDEX) return true;
+ // This check is needed in case the flag is toggled after a job has been registered.
+ if (!mShouldThrottle) return true;
+
+ // Quota constraint is not enforced while charging or when parole is on.
+ return mChargeTracker.isCharging() || mInParole
+ || getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0;
+ }
+
+ @VisibleForTesting
+ long getRemainingExecutionTimeLocked(@NonNull final JobStatus jobStatus) {
+ return getRemainingExecutionTimeLocked(jobStatus.getSourceUserId(),
+ jobStatus.getSourcePackageName(),
+ getEffectiveStandbyBucket(jobStatus));
+ }
+
+ @VisibleForTesting
+ long getRemainingExecutionTimeLocked(final int userId, @NonNull final String packageName) {
+ final int standbyBucket = JobSchedulerService.standbyBucketForPackage(packageName,
+ userId, sElapsedRealtimeClock.millis());
+ return getRemainingExecutionTimeLocked(userId, packageName, standbyBucket);
+ }
+
+ /**
+ * Returns the amount of time, in milliseconds, that this job has remaining to run based on its
+ * current standby bucket. Time remaining could be negative if the app was moved from a less
+ * restricted to a more restricted bucket.
+ */
+ private long getRemainingExecutionTimeLocked(final int userId,
+ @NonNull final String packageName, final int standbyBucket) {
+ if (standbyBucket == NEVER_INDEX) {
+ return 0;
+ }
+ final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+ final long trailingRunDurationMs = getTrailingExecutionTimeLocked(
+ userId, packageName, bucketWindowSizeMs);
+ return mAllowedTimePerPeriodMs - trailingRunDurationMs;
+ }
+
+ /** Returns how long the uid has had jobs running within the most recent window. */
+ @VisibleForTesting
+ long getTrailingExecutionTimeLocked(final int userId, @NonNull final String packageName,
+ final long windowSizeMs) {
+ long totalTime = 0;
+
+ Timer timer = mPkgTimers.get(userId, packageName);
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (timer != null && timer.isActive()) {
+ totalTime = timer.getCurrentDuration(nowElapsed);
+ }
+
+ List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+ if (sessions == null || sessions.size() == 0) {
+ return totalTime;
+ }
+
+ final long startElapsed = nowElapsed - windowSizeMs;
+ // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
+ // the most recent ones.
+ for (int i = sessions.size() - 1; i >= 0; --i) {
+ TimingSession session = sessions.get(i);
+ if (startElapsed < session.startTimeElapsed) {
+ totalTime += session.endTimeElapsed - session.startTimeElapsed;
+ } else if (startElapsed < session.endTimeElapsed) {
+ // The session started before the window but ended within the window. Only include
+ // the portion that was within the window.
+ totalTime += session.endTimeElapsed - startElapsed;
+ } else {
+ // This session ended before the window. No point in going any further.
+ return totalTime;
+ }
+ }
+ return totalTime;
+ }
+
+ @VisibleForTesting
+ void saveTimingSession(final int userId, @NonNull final String packageName,
+ @NonNull final TimingSession session) {
+ synchronized (mLock) {
+ List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+ if (sessions == null) {
+ sessions = new ArrayList<>();
+ mTimingSessions.add(userId, packageName, sessions);
+ }
+ sessions.add(session);
+
+ maybeScheduleCleanupAlarmLocked();
+ }
+ }
+
+ private final class EarliestEndTimeFunctor implements Consumer<List<TimingSession>> {
+ public long earliestEndElapsed = Long.MAX_VALUE;
+
+ @Override
+ public void accept(List<TimingSession> sessions) {
+ if (sessions != null && sessions.size() > 0) {
+ earliestEndElapsed = Math.min(earliestEndElapsed, sessions.get(0).endTimeElapsed);
+ }
+ }
+
+ void reset() {
+ earliestEndElapsed = Long.MAX_VALUE;
+ }
+ }
+
+ private final EarliestEndTimeFunctor mEarliestEndTimeFunctor = new EarliestEndTimeFunctor();
+
+ /** Schedule a cleanup alarm if necessary and there isn't already one scheduled. */
+ @VisibleForTesting
+ void maybeScheduleCleanupAlarmLocked() {
+ if (mNextCleanupTimeElapsed > sElapsedRealtimeClock.millis()) {
+ // There's already an alarm scheduled. Just stick with that one. There's no way we'll
+ // end up scheduling an earlier alarm.
+ if (DEBUG) {
+ Slog.v(TAG, "Not scheduling cleanup since there's already one at "
+ + mNextCleanupTimeElapsed + " (in " + (mNextCleanupTimeElapsed
+ - sElapsedRealtimeClock.millis()) + "ms)");
+ }
+ return;
+ }
+ mEarliestEndTimeFunctor.reset();
+ mTimingSessions.forEach(mEarliestEndTimeFunctor);
+ final long earliestEndElapsed = mEarliestEndTimeFunctor.earliestEndElapsed;
+ if (earliestEndElapsed == Long.MAX_VALUE) {
+ // Couldn't find a good time to clean up. Maybe this was called after we deleted all
+ // timing sessions.
+ if (DEBUG) Slog.d(TAG, "Didn't find a time to schedule cleanup");
+ return;
+ }
+ // Need to keep sessions for all apps up to the max period, regardless of their current
+ // standby bucket.
+ long nextCleanupElapsed = earliestEndElapsed + MAX_PERIOD_MS;
+ if (nextCleanupElapsed - mNextCleanupTimeElapsed <= 10 * MINUTE_IN_MILLIS) {
+ // No need to clean up too often. Delay the alarm if the next cleanup would be too soon
+ // after it.
+ nextCleanupElapsed += 10 * MINUTE_IN_MILLIS;
+ }
+ mNextCleanupTimeElapsed = nextCleanupElapsed;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextCleanupElapsed, ALARM_TAG_CLEANUP,
+ mSessionCleanupAlarmListener, mHandler);
+ if (DEBUG) Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed);
+ }
+
+ private void handleNewChargingStateLocked() {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final boolean isCharging = mChargeTracker.isCharging();
+ if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
+ // Deal with Timers first.
+ mPkgTimers.forEach((t) -> t.onChargingChanged(nowElapsed, isCharging));
+ // Now update jobs.
+ maybeUpdateAllConstraintsLocked();
+ }
+
+ private void maybeUpdateAllConstraintsLocked() {
+ boolean changed = false;
+ for (int u = 0; u < mTrackedJobs.numUsers(); ++u) {
+ final int userId = mTrackedJobs.keyAt(u);
+ for (int p = 0; p < mTrackedJobs.numPackagesForUser(userId); ++p) {
+ final String packageName = mTrackedJobs.keyAt(u, p);
+ changed |= maybeUpdateConstraintForPkgLocked(userId, packageName);
+ }
+ }
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+
+ /**
+ * Update the CONSTRAINT_WITHIN_QUOTA bit for all of the Jobs for a given package.
+ *
+ * @return true if at least one job had its bit changed
+ */
+ private boolean maybeUpdateConstraintForPkgLocked(final int userId,
+ @NonNull final String packageName) {
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
+ if (jobs == null || jobs.size() == 0) {
+ return false;
+ }
+
+ // Quota is the same for all jobs within a package.
+ final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket();
+ final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket);
+ boolean changed = false;
+ for (int i = jobs.size() - 1; i >= 0; --i) {
+ final JobStatus js = jobs.valueAt(i);
+ if (js.uidActive) {
+ // Jobs for the active app should always be able to run.
+ changed |= js.setQuotaConstraintSatisfied(true);
+ } else if (realStandbyBucket == getEffectiveStandbyBucket(js)) {
+ changed |= js.setQuotaConstraintSatisfied(realInQuota);
+ } else {
+ // This job is somehow exempted. Need to determine its own quota status.
+ changed |= js.setQuotaConstraintSatisfied(isWithinQuotaLocked(js));
+ }
+ }
+ if (!realInQuota) {
+ // Don't want to use the effective standby bucket here since that bump the bucket to
+ // ACTIVE for one of the jobs, which doesn't help with other jobs that aren't
+ // exempted.
+ maybeScheduleStartAlarmLocked(userId, packageName, realStandbyBucket);
+ } else {
+ QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+ if (alarmListener != null) {
+ mAlarmManager.cancel(alarmListener);
+ // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
+ alarmListener.setTriggerTime(0);
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Maybe schedule a non-wakeup alarm for the next time this package will have quota to run
+ * again. This should only be called if the package is already out of quota.
+ */
+ @VisibleForTesting
+ void maybeScheduleStartAlarmLocked(final int userId, @NonNull final String packageName,
+ final int standbyBucket) {
+ final String pkgString = string(userId, packageName);
+ if (standbyBucket == NEVER_INDEX) {
+ return;
+ } else if (standbyBucket == ACTIVE_INDEX) {
+ // ACTIVE apps are "always" in quota.
+ if (DEBUG) {
+ Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+ + " even though it is active");
+ }
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
+
+ QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+ if (alarmListener != null) {
+ // Cancel any pending alarm.
+ mAlarmManager.cancel(alarmListener);
+ // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
+ alarmListener.setTriggerTime(0);
+ }
+ return;
+ }
+
+ List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+ if (sessions == null || sessions.size() == 0) {
+ // If there are no sessions, then the job is probably in quota.
+ if (DEBUG) {
+ Slog.wtf(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+ + " even though it is likely within its quota.");
+ }
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
+ return;
+ }
+
+ final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ // How far back we need to look.
+ final long startElapsed = nowElapsed - bucketWindowSizeMs;
+
+ long totalTime = 0;
+ long cutoffTimeElapsed = nowElapsed;
+ for (int i = sessions.size() - 1; i >= 0; i--) {
+ TimingSession session = sessions.get(i);
+ if (startElapsed < session.startTimeElapsed) {
+ cutoffTimeElapsed = session.startTimeElapsed;
+ totalTime += session.endTimeElapsed - session.startTimeElapsed;
+ } else if (startElapsed < session.endTimeElapsed) {
+ // The session started before the window but ended within the window. Only
+ // include the portion that was within the window.
+ cutoffTimeElapsed = startElapsed;
+ totalTime += session.endTimeElapsed - startElapsed;
+ } else {
+ // This session ended before the window. No point in going any further.
+ break;
+ }
+ if (totalTime >= mAllowedTimePerPeriodMs) {
+ break;
+ }
+ }
+ if (totalTime < mAllowedTimePerPeriodMs) {
+ // Already in quota. Why was this method called?
+ if (DEBUG) {
+ Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+ + " even though it already has " + (mAllowedTimePerPeriodMs - totalTime)
+ + "ms in its quota.");
+ }
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
+ return;
+ }
+
+ QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+ if (alarmListener == null) {
+ alarmListener = new QcAlarmListener(userId, packageName);
+ mInQuotaAlarmListeners.add(userId, packageName, alarmListener);
+ }
+
+ // We add all the way back to the beginning of a session (or the window) even when we don't
+ // need to (in order to simplify the for loop above), so there might be some extra we
+ // need to add back.
+ final long extraTimeMs = totalTime - mAllowedTimePerPeriodMs;
+ // The time this app will have quota again.
+ final long inQuotaTimeElapsed =
+ cutoffTimeElapsed + extraTimeMs + mQuotaBufferMs + bucketWindowSizeMs;
+ // Only schedule the alarm if:
+ // 1. There isn't one currently scheduled
+ // 2. The new alarm is significantly earlier than the previous alarm (which could be the
+ // case if the package moves into a higher standby bucket). If it's earlier but not
+ // significantly so, then we essentially delay the job a few extra minutes.
+ // 3. The alarm is after the current alarm by more than the quota buffer.
+ // TODO: this might be overengineering. Simplify if proven safe.
+ if (!alarmListener.isWaiting()
+ || inQuotaTimeElapsed < alarmListener.getTriggerTimeElapsed() - 3 * MINUTE_IN_MILLIS
+ || alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed - mQuotaBufferMs) {
+ if (DEBUG) Slog.d(TAG, "Scheduling start alarm for " + pkgString);
+ // If the next time this app will have quota is at least 3 minutes before the
+ // alarm is supposed to go off, reschedule the alarm.
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, inQuotaTimeElapsed,
+ ALARM_TAG_QUOTA_CHECK, alarmListener, mHandler);
+ alarmListener.setTriggerTime(inQuotaTimeElapsed);
+ }
+ }
+
+ private final class ChargingTracker extends BroadcastReceiver {
+ /**
+ * Track whether we're charging. This has a slightly different definition than that of
+ * BatteryController.
+ */
+ private boolean mCharging;
+
+ ChargingTracker() {
+ }
+
+ public void startTracking() {
+ IntentFilter filter = new IntentFilter();
+
+ // Charging/not charging.
+ filter.addAction(BatteryManager.ACTION_CHARGING);
+ filter.addAction(BatteryManager.ACTION_DISCHARGING);
+ mContext.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ BatteryManagerInternal batteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
+ mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+ }
+
+ public boolean isCharging() {
+ return mCharging;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ final String action = intent.getAction();
+ if (BatteryManager.ACTION_CHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received charging intent, fired @ "
+ + sElapsedRealtimeClock.millis());
+ }
+ mCharging = true;
+ handleNewChargingStateLocked();
+ } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Disconnected from power.");
+ }
+ mCharging = false;
+ handleNewChargingStateLocked();
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static final class TimingSession {
+ // Start timestamp in elapsed realtime timebase.
+ public final long startTimeElapsed;
+ // End timestamp in elapsed realtime timebase.
+ public final long endTimeElapsed;
+ // How many background jobs ran during this session.
+ public final int bgJobCount;
+
+ TimingSession(long startElapsed, long endElapsed, int jobCount) {
+ this.startTimeElapsed = startElapsed;
+ this.endTimeElapsed = endElapsed;
+ this.bgJobCount = jobCount;
+ }
+
+ @Override
+ public String toString() {
+ return "TimingSession{" + startTimeElapsed + "->" + endTimeElapsed + ", " + bgJobCount
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TimingSession) {
+ TimingSession other = (TimingSession) obj;
+ return startTimeElapsed == other.startTimeElapsed
+ && endTimeElapsed == other.endTimeElapsed
+ && bgJobCount == other.bgJobCount;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(new long[] {startTimeElapsed, endTimeElapsed, bgJobCount});
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.print(startTimeElapsed);
+ pw.print(" -> ");
+ pw.print(endTimeElapsed);
+ pw.print(" (");
+ pw.print(endTimeElapsed - startTimeElapsed);
+ pw.print("), ");
+ pw.print(bgJobCount);
+ pw.print(" bg jobs.");
+ pw.println();
+ }
+
+ public void dump(@NonNull ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(StateControllerProto.QuotaController.TimingSession.START_TIME_ELAPSED,
+ startTimeElapsed);
+ proto.write(StateControllerProto.QuotaController.TimingSession.END_TIME_ELAPSED,
+ endTimeElapsed);
+ proto.write(StateControllerProto.QuotaController.TimingSession.BG_JOB_COUNT,
+ bgJobCount);
+
+ proto.end(token);
+ }
+ }
+
+ private final class Timer {
+ private final Package mPkg;
+
+ // List of jobs currently running for this app that started when the app wasn't in the
+ // foreground.
+ private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>();
+ private long mStartTimeElapsed;
+ private int mBgJobCount;
+
+ Timer(int userId, String packageName) {
+ mPkg = new Package(userId, packageName);
+ }
+
+ void startTrackingJob(@NonNull JobStatus jobStatus) {
+ if (jobStatus.uidActive) {
+ // We intentionally don't pay attention to fg state changes after a job has started.
+ if (DEBUG) {
+ Slog.v(TAG,
+ "Timer ignoring " + jobStatus.toShortString() + " because uidActive");
+ }
+ return;
+ }
+ if (DEBUG) Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
+ synchronized (mLock) {
+ // Always track jobs, even when charging.
+ mRunningBgJobs.add(jobStatus);
+ if (!mChargeTracker.isCharging()) {
+ mBgJobCount++;
+ if (mRunningBgJobs.size() == 1) {
+ // Started tracking the first job.
+ mStartTimeElapsed = sElapsedRealtimeClock.millis();
+ scheduleCutoff();
+ }
+ }
+ }
+ }
+
+ void stopTrackingJob(@NonNull JobStatus jobStatus) {
+ if (DEBUG) Slog.v(TAG, "Stopping tracking of " + jobStatus.toShortString());
+ synchronized (mLock) {
+ if (mRunningBgJobs.size() == 0) {
+ // maybeStopTrackingJobLocked can be called when an app cancels a job, so a
+ // timer may not be running when it's asked to stop tracking a job.
+ if (DEBUG) {
+ Slog.d(TAG, "Timer isn't tracking any jobs but still told to stop");
+ }
+ return;
+ }
+ if (mRunningBgJobs.remove(jobStatus)
+ && !mChargeTracker.isCharging() && mRunningBgJobs.size() == 0) {
+ emitSessionLocked(sElapsedRealtimeClock.millis());
+ cancelCutoff();
+ }
+ }
+ }
+
+ private void emitSessionLocked(long nowElapsed) {
+ if (mBgJobCount <= 0) {
+ // Nothing to emit.
+ return;
+ }
+ TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mBgJobCount);
+ saveTimingSession(mPkg.userId, mPkg.packageName, ts);
+ mBgJobCount = 0;
+ // Don't reset the tracked jobs list as we need to keep tracking the current number
+ // of jobs.
+ // However, cancel the currently scheduled cutoff since it's not currently useful.
+ cancelCutoff();
+ }
+
+ /**
+ * Returns true if the Timer is actively tracking, as opposed to passively ref counting
+ * during charging.
+ */
+ public boolean isActive() {
+ synchronized (mLock) {
+ return mBgJobCount > 0;
+ }
+ }
+
+ long getCurrentDuration(long nowElapsed) {
+ synchronized (mLock) {
+ return !isActive() ? 0 : nowElapsed - mStartTimeElapsed;
+ }
+ }
+
+ void onChargingChanged(long nowElapsed, boolean isCharging) {
+ synchronized (mLock) {
+ if (isCharging) {
+ emitSessionLocked(nowElapsed);
+ } else {
+ // Start timing from unplug.
+ if (mRunningBgJobs.size() > 0) {
+ mStartTimeElapsed = nowElapsed;
+ // NOTE: this does have the unfortunate consequence that if the device is
+ // repeatedly plugged in and unplugged, the job count for a package may be
+ // artificially high.
+ mBgJobCount = mRunningBgJobs.size();
+ // Schedule cutoff since we're now actively tracking for quotas again.
+ scheduleCutoff();
+ }
+ }
+ }
+ }
+
+ void rescheduleCutoff() {
+ cancelCutoff();
+ scheduleCutoff();
+ }
+
+ private void scheduleCutoff() {
+ // Each package can only be in one standby bucket, so we only need to have one
+ // message per timer. We only need to reschedule when restarting timer or when
+ // standby bucket changes.
+ synchronized (mLock) {
+ if (!isActive()) {
+ return;
+ }
+ Message msg = mHandler.obtainMessage(MSG_REACHED_QUOTA, mPkg);
+ final long timeRemainingMs = getRemainingExecutionTimeLocked(mPkg.userId,
+ mPkg.packageName);
+ if (DEBUG) {
+ Slog.i(TAG, "Job for " + mPkg + " has " + timeRemainingMs + "ms left.");
+ }
+ // If the job was running the entire time, then the system would be up, so it's
+ // fine to use uptime millis for these messages.
+ mHandler.sendMessageDelayed(msg, timeRemainingMs);
+ }
+ }
+
+ private void cancelCutoff() {
+ mHandler.removeMessages(MSG_REACHED_QUOTA, mPkg);
+ }
+
+ public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
+ pw.print("Timer{");
+ pw.print(mPkg);
+ pw.print("} ");
+ if (isActive()) {
+ pw.print("started at ");
+ pw.print(mStartTimeElapsed);
+ } else {
+ pw.print("NOT active");
+ }
+ pw.print(", ");
+ pw.print(mBgJobCount);
+ pw.print(" running bg jobs");
+ pw.println();
+ pw.increaseIndent();
+ for (int i = 0; i < mRunningBgJobs.size(); i++) {
+ JobStatus js = mRunningBgJobs.valueAt(i);
+ if (predicate.test(js)) {
+ pw.println(js.toShortString());
+ }
+ }
+
+ pw.decreaseIndent();
+ }
+
+ public void dump(ProtoOutputStream proto, long fieldId, Predicate<JobStatus> predicate) {
+ final long token = proto.start(fieldId);
+
+ mPkg.writeToProto(proto, StateControllerProto.QuotaController.Timer.PKG);
+ proto.write(StateControllerProto.QuotaController.Timer.IS_ACTIVE, isActive());
+ proto.write(StateControllerProto.QuotaController.Timer.START_TIME_ELAPSED,
+ mStartTimeElapsed);
+ proto.write(StateControllerProto.QuotaController.Timer.BG_JOB_COUNT, mBgJobCount);
+ for (int i = 0; i < mRunningBgJobs.size(); i++) {
+ JobStatus js = mRunningBgJobs.valueAt(i);
+ if (predicate.test(js)) {
+ js.writeToShortProto(proto,
+ StateControllerProto.QuotaController.Timer.RUNNING_JOBS);
+ }
+ }
+
+ proto.end(token);
+ }
+ }
+
+ /**
+ * Tracking of app assignments to standby buckets
+ */
+ final class StandbyTracker extends AppIdleStateChangeListener {
+
+ @Override
+ public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
+ boolean idle, int bucket, int reason) {
+ // Update job bookkeeping out of band.
+ BackgroundThread.getHandler().post(() -> {
+ final int bucketIndex = JobSchedulerService.standbyBucketToBucketIndex(bucket);
+ if (DEBUG) {
+ Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex "
+ + bucketIndex);
+ }
+ synchronized (mLock) {
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
+ if (jobs == null || jobs.size() == 0) {
+ return;
+ }
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ JobStatus js = jobs.valueAt(i);
+ js.setStandbyBucket(bucketIndex);
+ }
+ Timer timer = mPkgTimers.get(userId, packageName);
+ if (timer != null && timer.isActive()) {
+ timer.rescheduleCutoff();
+ }
+ if (!mShouldThrottle || maybeUpdateConstraintForPkgLocked(userId,
+ packageName)) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onParoleStateChanged(final boolean isParoleOn) {
+ mInParole = isParoleOn;
+ if (DEBUG) Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
+ // Update job bookkeeping out of band.
+ BackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ maybeUpdateAllConstraintsLocked();
+ }
+ });
+ }
+ }
+
+ private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> {
+ private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() {
+ public boolean test(TimingSession ts) {
+ return ts.endTimeElapsed <= sElapsedRealtimeClock.millis() - MAX_PERIOD_MS;
+ }
+ };
+
+ @Override
+ public void accept(List<TimingSession> sessions) {
+ if (sessions != null) {
+ // Remove everything older than MAX_PERIOD_MS time ago.
+ sessions.removeIf(mTooOld);
+ }
+ }
+ }
+
+ private final DeleteTimingSessionsFunctor mDeleteOldSessionsFunctor =
+ new DeleteTimingSessionsFunctor();
+
+ @VisibleForTesting
+ void deleteObsoleteSessionsLocked() {
+ mTimingSessions.forEach(mDeleteOldSessionsFunctor);
+ }
+
+ private class QcHandler extends Handler {
+ QcHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mLock) {
+ switch (msg.what) {
+ case MSG_REACHED_QUOTA: {
+ Package pkg = (Package) msg.obj;
+ if (DEBUG) Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
+
+ long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId,
+ pkg.packageName);
+ if (timeRemainingMs <= 50) {
+ // Less than 50 milliseconds left. Start process of shutting down jobs.
+ if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
+ if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ } else {
+ // This could potentially happen if an old session phases out while a
+ // job is currently running.
+ // Reschedule message
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg);
+ if (DEBUG) {
+ Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left.");
+ }
+ sendMessageDelayed(rescheduleMsg, timeRemainingMs);
+ }
+ break;
+ }
+ case MSG_CLEAN_UP_SESSIONS:
+ if (DEBUG) Slog.d(TAG, "Cleaning up timing sessions.");
+ deleteObsoleteSessionsLocked();
+ maybeScheduleCleanupAlarmLocked();
+
+ break;
+ case MSG_CHECK_PACKAGE: {
+ String packageName = (String) msg.obj;
+ int userId = msg.arg1;
+ if (DEBUG) Slog.d(TAG, "Checking pkg " + string(userId, packageName));
+ if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private class QcAlarmListener implements AlarmManager.OnAlarmListener {
+ private final int mUserId;
+ private final String mPackageName;
+ private volatile long mTriggerTimeElapsed;
+
+ QcAlarmListener(int userId, String packageName) {
+ mUserId = userId;
+ mPackageName = packageName;
+ }
+
+ boolean isWaiting() {
+ return mTriggerTimeElapsed > 0;
+ }
+
+ void setTriggerTime(long timeElapsed) {
+ mTriggerTimeElapsed = timeElapsed;
+ }
+
+ long getTriggerTimeElapsed() {
+ return mTriggerTimeElapsed;
+ }
+
+ @Override
+ public void onAlarm() {
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE, mUserId, 0, mPackageName).sendToTarget();
+ mTriggerTimeElapsed = 0;
+ }
+ }
+
+ //////////////////////// TESTING HELPERS /////////////////////////////
+
+ @VisibleForTesting
+ long getAllowedTimePerPeriodMs() {
+ return mAllowedTimePerPeriodMs;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ long[] getBucketWindowSizes() {
+ return mBucketPeriodsMs;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @VisibleForTesting
+ long getInQuotaBufferMs() {
+ return mQuotaBufferMs;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ List<TimingSession> getTimingSessions(int userId, String packageName) {
+ return mTimingSessions.get(userId, packageName);
+ }
+
+ //////////////////////////// DATA DUMP //////////////////////////////
+
+ @Override
+ public void dumpControllerStateLocked(final IndentingPrintWriter pw,
+ final Predicate<JobStatus> predicate) {
+ pw.println("Is throttling: " + mShouldThrottle);
+ pw.println("Is charging: " + mChargeTracker.isCharging());
+ pw.println("In parole: " + mInParole);
+ pw.println();
+
+ mTrackedJobs.forEach((jobs) -> {
+ for (int j = 0; j < jobs.size(); j++) {
+ final JobStatus js = jobs.valueAt(j);
+ if (!predicate.test(js)) {
+ continue;
+ }
+ pw.print("#");
+ js.printUniqueId(pw);
+ pw.print(" from ");
+ UserHandle.formatUid(pw, js.getSourceUid());
+ pw.println();
+
+ pw.increaseIndent();
+ pw.print(JobStatus.bucketName(getEffectiveStandbyBucket(js)));
+ pw.print(", ");
+ if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
+ pw.print("within quota");
+ } else {
+ pw.print("not within quota");
+ }
+ pw.print(", ");
+ pw.print(getRemainingExecutionTimeLocked(js));
+ pw.print("ms remaining in quota");
+ pw.decreaseIndent();
+ pw.println();
+ }
+ });
+
+ pw.println();
+ for (int u = 0; u < mPkgTimers.numUsers(); ++u) {
+ final int userId = mPkgTimers.keyAt(u);
+ for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) {
+ final String pkgName = mPkgTimers.keyAt(u, p);
+ mPkgTimers.valueAt(u, p).dump(pw, predicate);
+ pw.println();
+ List<TimingSession> sessions = mTimingSessions.get(userId, pkgName);
+ if (sessions != null) {
+ pw.increaseIndent();
+ pw.println("Saved sessions:");
+ pw.increaseIndent();
+ for (int j = sessions.size() - 1; j >= 0; j--) {
+ TimingSession session = sessions.get(j);
+ session.dump(pw);
+ }
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+ Predicate<JobStatus> predicate) {
+ final long token = proto.start(fieldId);
+ final long mToken = proto.start(StateControllerProto.QUOTA);
+
+ proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging());
+ proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole);
+
+ mTrackedJobs.forEach((jobs) -> {
+ for (int j = 0; j < jobs.size(); j++) {
+ final JobStatus js = jobs.valueAt(j);
+ if (!predicate.test(js)) {
+ continue;
+ }
+ final long jsToken = proto.start(
+ StateControllerProto.QuotaController.TRACKED_JOBS);
+ js.writeToShortProto(proto,
+ StateControllerProto.QuotaController.TrackedJob.INFO);
+ proto.write(StateControllerProto.QuotaController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ proto.write(
+ StateControllerProto.QuotaController.TrackedJob.EFFECTIVE_STANDBY_BUCKET,
+ getEffectiveStandbyBucket(js));
+ proto.write(StateControllerProto.QuotaController.TrackedJob.HAS_QUOTA,
+ js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS,
+ getRemainingExecutionTimeLocked(js));
+ proto.end(jsToken);
+ }
+ });
+
+ for (int u = 0; u < mPkgTimers.numUsers(); ++u) {
+ final int userId = mPkgTimers.keyAt(u);
+ for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) {
+ final String pkgName = mPkgTimers.keyAt(u, p);
+ final long psToken = proto.start(
+ StateControllerProto.QuotaController.PACKAGE_STATS);
+ mPkgTimers.valueAt(u, p).dump(proto,
+ StateControllerProto.QuotaController.PackageStats.TIMER, predicate);
+
+ List<TimingSession> sessions = mTimingSessions.get(userId, pkgName);
+ if (sessions != null) {
+ for (int j = sessions.size() - 1; j >= 0; j--) {
+ TimingSession session = sessions.get(j);
+ session.dump(proto,
+ StateControllerProto.QuotaController.PackageStats.SAVED_SESSIONS);
+ }
+ }
+
+ proto.end(psToken);
+ }
+ }
+
+ proto.end(mToken);
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index c2be283..61dc479 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -16,7 +16,10 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.DEBUG;
+
import android.content.Context;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
@@ -32,6 +35,8 @@
* are ready to run, or whether they must be stopped.
*/
public abstract class StateController {
+ private static final String TAG = "JobScheduler.SC";
+
protected final JobSchedulerService mService;
protected final StateChangedListener mStateChangedListener;
protected final Context mContext;
@@ -53,22 +58,62 @@
* preexisting tasks.
*/
public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob);
+
/**
* Optionally implement logic here to prepare the job to be executed.
*/
public void prepareForExecutionLocked(JobStatus jobStatus) {
}
+
/**
* Remove task - this will happen if the task is cancelled, completed, etc.
*/
public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate);
+
/**
* Called when a new job is being created to reschedule an old failed job.
*/
public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) {
}
+ /**
+ * Called when the JobScheduler.Constants are updated.
+ */
+ public void onConstantsUpdatedLocked() {
+ }
+
+ protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
+ // This is very cheap to check (just a few conditions on data in JobStatus).
+ final boolean jobWouldBeReady = jobStatus.wouldBeReadyWithConstraint(constraint);
+ if (DEBUG) {
+ Slog.v(TAG, "wouldBeReadyWithConstraintLocked: " + jobStatus.toShortString()
+ + " readyWithConstraint=" + jobWouldBeReady);
+ }
+ if (!jobWouldBeReady) {
+ // If the job wouldn't be ready, nothing to do here.
+ return false;
+ }
+
+ // This is potentially more expensive since JSS may have to query component
+ // presence.
+ return mService.areComponentsInPlaceLocked(jobStatus);
+ }
+
+ /**
+ * Called when JobSchedulerService has determined that the job is not ready to be run. The
+ * Controller can evaluate if it can or should do something to promote this job's readiness.
+ */
+ public void evaluateStateLocked(JobStatus jobStatus) {
+ }
+
+ /**
+ * Called when something with the UID has changed. The controller should re-evaluate any
+ * internal state tracking dependent on this UID.
+ */
+ public void reevaluateStateLocked(int uid) {
+ }
+
public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate);
public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
new file mode 100644
index 0000000..4c7c420
--- /dev/null
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.Location;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * @hide
+ */
+public abstract class AbstractLocationProvider {
+
+ /**
+ * Interface for communicating from a location provider back to the location service.
+ */
+ public interface LocationProviderManager {
+
+ /**
+ * Called on location provider construction to make the location service aware of this
+ * provider and what it's initial enabled/disabled state should be.
+ */
+ void onAttachProvider(AbstractLocationProvider locationProvider, boolean initiallyEnabled);
+
+ /**
+ * May be called to inform the location service of a change in this location provider's
+ * enabled/disabled state.
+ */
+ void onSetEnabled(boolean enabled);
+
+ /**
+ * May be called to inform the location service of a change in this location provider's
+ * properties.
+ */
+ void onSetProperties(ProviderProperties properties);
+
+ /**
+ * May be called to inform the location service that this provider has a new location
+ * available.
+ */
+ void onReportLocation(Location location);
+
+ /**
+ * May be called to inform the location service that this provider has a new location
+ * available.
+ */
+ void onReportLocation(List<Location> locations);
+ }
+
+ private final LocationProviderManager mLocationProviderManager;
+
+ protected AbstractLocationProvider(LocationProviderManager locationProviderManager) {
+ this(locationProviderManager, true);
+ }
+
+ protected AbstractLocationProvider(LocationProviderManager locationProviderManager,
+ boolean initiallyEnabled) {
+ mLocationProviderManager = locationProviderManager;
+ mLocationProviderManager.onAttachProvider(this, initiallyEnabled);
+ }
+
+ /**
+ * Call this method to report a new location. May be called from any thread.
+ */
+ protected void reportLocation(Location location) {
+ mLocationProviderManager.onReportLocation(location);
+ }
+
+ /**
+ * Call this method to report a new location. May be called from any thread.
+ */
+ protected void reportLocation(List<Location> locations) {
+ mLocationProviderManager.onReportLocation(locations);
+ }
+
+ /**
+ * Call this method to report a change in provider enabled/disabled status. May be called from
+ * any thread.
+ */
+ protected void setEnabled(boolean enabled) {
+ mLocationProviderManager.onSetEnabled(enabled);
+ }
+
+ /**
+ * Call this method to report a change in provider properties. May be called from
+ * any thread.
+ */
+ protected void setProperties(ProviderProperties properties) {
+ mLocationProviderManager.onSetProperties(properties);
+ }
+
+ /**
+ * Called when the location service delivers a new request for fulfillment to the provider.
+ * Replaces any previous requests completely.
+ */
+ public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+ /**
+ * Called to dump debug or log information.
+ */
+ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ /**
+ * Retrieves the current status of the provider.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public int getStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
+
+ /**
+ * Retrieves the last update time of the status of the provider.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public long getStatusUpdateTime() {
+ return 0;
+ }
+
+ /** Sends a custom command to this provider. */
+ public abstract void sendExtraCommand(String command, Bundle extras);
+}
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 6612d02..675e59e 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -237,7 +237,7 @@
Supplier<Intent> supplier =
() -> createIntent(ContextHubManager.EVENT_NANOAPP_MESSAGE, message.getNanoAppId())
.putExtra(ContextHubManager.EXTRA_MESSAGE, message);
- sendPendingIntent(supplier);
+ sendPendingIntent(supplier, message.getNanoAppId());
}
/**
@@ -247,7 +247,8 @@
*/
/* package */ void onNanoAppLoaded(long nanoAppId) {
invokeCallback(callback -> callback.onNanoAppLoaded(nanoAppId));
- sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_NANOAPP_LOADED, nanoAppId));
+ sendPendingIntent(
+ () -> createIntent(ContextHubManager.EVENT_NANOAPP_LOADED, nanoAppId), nanoAppId);
}
/**
@@ -257,7 +258,8 @@
*/
/* package */ void onNanoAppUnloaded(long nanoAppId) {
invokeCallback(callback -> callback.onNanoAppUnloaded(nanoAppId));
- sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_NANOAPP_UNLOADED, nanoAppId));
+ sendPendingIntent(
+ () -> createIntent(ContextHubManager.EVENT_NANOAPP_UNLOADED, nanoAppId), nanoAppId);
}
/**
@@ -280,7 +282,7 @@
Supplier<Intent> supplier =
() -> createIntent(ContextHubManager.EVENT_NANOAPP_ABORTED, nanoAppId)
.putExtra(ContextHubManager.EXTRA_NANOAPP_ABORT_CODE, abortCode);
- sendPendingIntent(supplier);
+ sendPendingIntent(supplier, nanoAppId);
}
/**
@@ -360,18 +362,40 @@
*/
private synchronized void sendPendingIntent(Supplier<Intent> supplier) {
if (mPendingIntentRequest.hasPendingIntent()) {
- Intent intent = supplier.get();
- try {
- mPendingIntentRequest.getPendingIntent().send(
- mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
- Manifest.permission.LOCATION_HARDWARE /* requiredPermission */,
- null /* options */);
- } catch (PendingIntent.CanceledException e) {
- // The PendingIntent is no longer valid
- Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
- + " (host endpoint ID " + mHostEndPointId + ")");
- close();
- }
+ doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get());
+ }
+ }
+
+ /**
+ * Sends an intent to any existing PendingIntent
+ *
+ * @param supplier method to create the extra Intent
+ * @param nanoAppId the ID of the nanoapp which this event is for
+ */
+ private synchronized void sendPendingIntent(Supplier<Intent> supplier, long nanoAppId) {
+ if (mPendingIntentRequest.hasPendingIntent()
+ && mPendingIntentRequest.getNanoAppId() == nanoAppId) {
+ doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get());
+ }
+ }
+
+ /**
+ * Sends a PendingIntent with extra Intent data
+ *
+ * @param pendingIntent the PendingIntent
+ * @param intent the extra Intent data
+ */
+ private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) {
+ try {
+ pendingIntent.send(
+ mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
+ Manifest.permission.LOCATION_HARDWARE /* requiredPermission */,
+ null /* options */);
+ } catch (PendingIntent.CanceledException e) {
+ // The PendingIntent is no longer valid
+ Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
+ + " (host endpoint ID " + mHostEndPointId + ")");
+ close();
}
}
diff --git a/services/core/java/com/android/server/location/GnssGeofenceProvider.java b/services/core/java/com/android/server/location/GnssGeofenceProvider.java
index 6ac4aeb..a84b0b1 100644
--- a/services/core/java/com/android/server/location/GnssGeofenceProvider.java
+++ b/services/core/java/com/android/server/location/GnssGeofenceProvider.java
@@ -1,18 +1,12 @@
package com.android.server.location;
import android.location.IGpsGeofenceHardware;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-
/**
* Manages GNSS Geofence operations.
*/
@@ -34,26 +28,26 @@
public boolean paused;
}
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private final GnssGeofenceProviderNative mNative;
+ @GuardedBy("mLock")
private final SparseArray<GeofenceEntry> mGeofenceEntries = new SparseArray<>();
- private final Handler mHandler;
- GnssGeofenceProvider(Looper looper) {
- this(looper, new GnssGeofenceProviderNative());
+ GnssGeofenceProvider() {
+ this(new GnssGeofenceProviderNative());
}
@VisibleForTesting
- GnssGeofenceProvider(Looper looper, GnssGeofenceProviderNative gnssGeofenceProviderNative) {
- mHandler = new Handler(looper);
+ GnssGeofenceProvider(GnssGeofenceProviderNative gnssGeofenceProviderNative) {
mNative = gnssGeofenceProviderNative;
}
- // TODO(b/37460011): use this method in HAL death recovery.
void resumeIfStarted() {
if (DEBUG) {
Log.d(TAG, "resumeIfStarted");
}
- mHandler.post(() -> {
+ synchronized (mLock) {
for (int i = 0; i < mGeofenceEntries.size(); i++) {
GeofenceEntry entry = mGeofenceEntries.valueAt(i);
boolean added = mNative.addGeofence(entry.geofenceId, entry.latitude,
@@ -65,30 +59,21 @@
mNative.pauseGeofence(entry.geofenceId);
}
}
- });
- }
-
- private boolean runOnHandlerThread(Callable<Boolean> callable) {
- FutureTask<Boolean> futureTask = new FutureTask<>(callable);
- mHandler.post(futureTask);
- try {
- return futureTask.get();
- } catch (InterruptedException | ExecutionException e) {
- Log.e(TAG, "Failed running callable.", e);
}
- return false;
}
@Override
public boolean isHardwareGeofenceSupported() {
- return runOnHandlerThread(mNative::isGeofenceSupported);
+ synchronized (mLock) {
+ return mNative.isGeofenceSupported();
+ }
}
@Override
public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
double longitude, double radius, int lastTransition, int monitorTransitions,
int notificationResponsiveness, int unknownTimer) {
- return runOnHandlerThread(() -> {
+ synchronized (mLock) {
boolean added = mNative.addGeofence(geofenceId, latitude, longitude, radius,
lastTransition, monitorTransitions, notificationResponsiveness,
unknownTimer);
@@ -105,23 +90,23 @@
mGeofenceEntries.put(geofenceId, entry);
}
return added;
- });
+ }
}
@Override
public boolean removeHardwareGeofence(int geofenceId) {
- return runOnHandlerThread(() -> {
+ synchronized (mLock) {
boolean removed = mNative.removeGeofence(geofenceId);
if (removed) {
mGeofenceEntries.remove(geofenceId);
}
return removed;
- });
+ }
}
@Override
public boolean pauseHardwareGeofence(int geofenceId) {
- return runOnHandlerThread(() -> {
+ synchronized (mLock) {
boolean paused = mNative.pauseGeofence(geofenceId);
if (paused) {
GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
@@ -130,12 +115,12 @@
}
}
return paused;
- });
+ }
}
@Override
public boolean resumeHardwareGeofence(int geofenceId, int monitorTransitions) {
- return runOnHandlerThread(() -> {
+ synchronized (mLock) {
boolean resumed = mNative.resumeGeofence(geofenceId, monitorTransitions);
if (resumed) {
GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
@@ -145,7 +130,7 @@
}
}
return resumed;
- });
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d5e4681..29e1878 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -31,10 +31,7 @@
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
-import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
import android.location.IGpsGeofenceHardware;
-import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationListener;
@@ -84,6 +81,10 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -97,8 +98,18 @@
*
* {@hide}
*/
-public class GnssLocationProvider extends LocationProviderInterface
- implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends AbstractLocationProvider implements
+ InjectNtpTimeCallback,
+ GnssSatelliteBlacklistCallback {
+
+ /**
+ * Indicates that this method is a native entry point. Useful purely for IDEs which can
+ * understand entry points, and thus eliminate incorrect warnings about methods not used.
+ */
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface NativeEntryPoint {
+ }
private static final String TAG = "GnssLocationProvider";
@@ -249,7 +260,7 @@
}
public void set(int svCount, int meanCn0, int maxCn0) {
- synchronized(this) {
+ synchronized (this) {
mSvCount = svCount;
mMeanCn0 = meanCn0;
mMaxCn0 = maxCn0;
@@ -258,7 +269,7 @@
}
public void reset() {
- set(0,0,0);
+ set(0, 0, 0);
}
// Also used by outside methods to add to other bundles
@@ -314,7 +325,7 @@
MAX_RETRY_INTERVAL);
// true if we are enabled, protected by this
- private boolean mEnabled;
+ private boolean mEnabled = true;
// states for injecting ntp and downloading xtra data
private static final int STATE_PENDING_NETWORK = 0;
@@ -328,9 +339,6 @@
// true if GPS is navigating
private boolean mNavigating;
- // true if GPS engine is on
- private boolean mEngineOn;
-
// requested frequency of fixes, in milliseconds
private int mFixInterval = 1000;
@@ -380,9 +388,8 @@
private boolean mSuplEsEnabled = false;
private final Context mContext;
- private final ILocationManager mILocationManager;
private final LocationExtras mLocationExtras = new LocationExtras();
- private final GnssStatusListenerHelper mListenerHelper;
+ private final GnssStatusListenerHelper mGnssStatusListenerHelper;
private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
@@ -443,20 +450,8 @@
// GNSS Metrics
private GnssMetrics mGnssMetrics;
- private final IGnssStatusProvider mGnssStatusProvider = new IGnssStatusProvider.Stub() {
- @Override
- public void registerGnssStatusCallback(IGnssStatusListener callback) {
- mListenerHelper.addListener(callback);
- }
-
- @Override
- public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
- mListenerHelper.removeListener(callback);
- }
- };
-
- public IGnssStatusProvider getGnssStatusProvider() {
- return mGnssStatusProvider;
+ public GnssStatusListenerHelper getGnssStatusProvider() {
+ return mGnssStatusListenerHelper;
}
public IGpsGeofenceHardware getGpsGeofenceProxy() {
@@ -479,17 +474,22 @@
return;
}
- if (action.equals(ALARM_WAKEUP)) {
- startNavigating(false);
- } else if (action.equals(ALARM_TIMEOUT)) {
- hibernate();
- } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
- || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)
- || Intent.ACTION_SCREEN_ON.equals(action)) {
- updateLowPowerMode();
- } else if (action.equals(SIM_STATE_CHANGED)) {
- subscriptionOrSimChanged(context);
+ switch (action) {
+ case ALARM_WAKEUP:
+ startNavigating(false);
+ break;
+ case ALARM_TIMEOUT:
+ hibernate();
+ break;
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+ case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ case Intent.ACTION_SCREEN_OFF:
+ case Intent.ACTION_SCREEN_ON:
+ updateLowPowerMode();
+ break;
+ case SIM_STATE_CHANGED:
+ subscriptionOrSimChanged(context);
+ break;
}
}
};
@@ -507,9 +507,7 @@
*/
@Override
public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
- mHandler.post(()->{
- native_set_satellite_blacklist(constellations, svids);
- });
+ mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
}
private void subscriptionOrSimChanged(Context context) {
@@ -572,7 +570,7 @@
}
interface SetCarrierProperty {
- public boolean set(int value);
+ boolean set(int value);
}
private void reloadGpsProperties(Context context, Properties properties) {
@@ -587,7 +585,7 @@
/*
* Overlay carrier properties from a debug configuration file.
*/
- loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties);
+ loadPropertiesFromFile(properties);
// TODO: we should get rid of C2K specific setting.
setSuplHostPort(properties.getProperty("SUPL_HOST"),
properties.getProperty("SUPL_PORT"));
@@ -603,15 +601,15 @@
if (native_is_gnss_configuration_supported()) {
Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
{
- put("SUPL_VER", (val) -> native_set_supl_version(val));
- put("SUPL_MODE", (val) -> native_set_supl_mode(val));
- put("SUPL_ES", (val) -> native_set_supl_es(val));
- put("LPP_PROFILE", (val) -> native_set_lpp_profile(val));
+ put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
+ put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
+ put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
+ put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
put("A_GLONASS_POS_PROTOCOL_SELECT",
- (val) -> native_set_gnss_pos_protocol_select(val));
+ GnssLocationProvider::native_set_gnss_pos_protocol_select);
put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
- (val) -> native_set_emergency_supl_pdn(val));
- put("GPS_LOCK", (val) -> native_set_gps_lock(val));
+ GnssLocationProvider::native_set_emergency_supl_pdn);
+ put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
}
};
@@ -622,7 +620,7 @@
try {
int propertyValueInt = Integer.decode(propertyValueString);
boolean result = entry.getValue().set(propertyValueInt);
- if (result == false) {
+ if (!result) {
Log.e(TAG, "Unable to set " + propertyName);
}
} catch (NumberFormatException e) {
@@ -664,10 +662,9 @@
}
}
- private boolean loadPropertiesFromFile(String filename,
- Properties properties) {
+ private void loadPropertiesFromFile(Properties properties) {
try {
- File file = new File(filename);
+ File file = new File(DEBUG_PROPERTIES_FILE);
FileInputStream stream = null;
try {
stream = new FileInputStream(file);
@@ -677,16 +674,15 @@
}
} catch (IOException e) {
- if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filename);
- return false;
+ if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
}
- return true;
}
- public GnssLocationProvider(Context context, ILocationManager ilocationManager,
+ public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
Looper looper) {
+ super(locationProviderManager, true);
+
mContext = context;
- mILocationManager = ilocationManager;
// Create a wake lock
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -730,7 +726,7 @@
mNetInitiatedListener,
mSuplEsEnabled);
- mListenerHelper = new GnssStatusListenerHelper(mHandler) {
+ mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
@Override
protected boolean isAvailableInPlatform() {
return isSupported();
@@ -749,7 +745,7 @@
}
};
- mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mHandler) {
+ mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mContext, mHandler) {
@Override
protected boolean isGpsEnabled() {
return isEnabled();
@@ -762,20 +758,21 @@
looper, this);
mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
mGnssBatchingProvider = new GnssBatchingProvider();
- mGnssGeofenceProvider = new GnssGeofenceProvider(looper);
- }
+ mGnssGeofenceProvider = new GnssGeofenceProvider();
- /**
- * Returns the name of this provider.
- */
- @Override
- public String getName() {
- return LocationManager.GPS_PROVIDER;
- }
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getSendingUserId() == UserHandle.USER_ALL) {
+ mEnabled = false;
+ handleDisable();
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, mHandler);
- @Override
- public ProviderProperties getProperties() {
- return PROPERTIES;
+ setProperties(PROPERTIES);
}
/**
@@ -840,9 +837,9 @@
locationManager.requestLocationUpdates(provider,
LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0,
locationListener, mHandler.getLooper());
- locationListener.numLocationUpdateRequest++;
+ locationListener.mNumLocationUpdateRequest++;
mHandler.postDelayed(() -> {
- if (--locationListener.numLocationUpdateRequest == 0) {
+ if (--locationListener.mNumLocationUpdateRequest == 0) {
Log.i(TAG,
String.format("Removing location updates from %s provider.", provider));
locationManager.removeUpdates(locationListener);
@@ -905,43 +902,40 @@
// hold wake lock while task runs
mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
- AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
- @Override
- public void run() {
- GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
- byte[] data = xtraDownloader.downloadXtraData();
- if (data != null) {
- if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
- native_inject_xtra_data(data, data.length);
- mXtraBackOff.reset();
- }
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
+ native_inject_xtra_data(data, data.length);
+ mXtraBackOff.reset();
+ }
- sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
+ sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
- if (data == null) {
- // try again later
- // since this is delayed and not urgent we do not hold a wake lock here
- mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
- mXtraBackOff.nextBackoffMillis());
- }
+ if (data == null) {
+ // try again later
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
+ mXtraBackOff.nextBackoffMillis());
+ }
- // Release wake lock held by task, synchronize on mLock in case multiple
- // download tasks overrun.
- synchronized (mLock) {
- if (mDownloadXtraWakeLock.isHeld()) {
- // This wakelock may have time-out, if a timeout was specified.
- // Catch (and ignore) any timeout exceptions.
- try {
- mDownloadXtraWakeLock.release();
- if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
- } catch (Exception e) {
- Log.i(TAG, "Wakelock timeout & release race exception in "
- + "handleDownloadXtraData()", e);
- }
- } else {
- Log.e(TAG, "WakeLock expired before release in "
- + "handleDownloadXtraData()");
+ // Release wake lock held by task, synchronize on mLock in case multiple
+ // download tasks overrun.
+ synchronized (mLock) {
+ if (mDownloadXtraWakeLock.isHeld()) {
+ // This wakelock may have time-out, if a timeout was specified.
+ // Catch (and ignore) any timeout exceptions.
+ try {
+ mDownloadXtraWakeLock.release();
+ if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+ } catch (Exception e) {
+ Log.i(TAG, "Wakelock timeout & release race exception in "
+ + "handleDownloadXtraData()", e);
}
+ } else {
+ Log.e(TAG, "WakeLock expired before release in "
+ + "handleDownloadXtraData()");
}
}
});
@@ -954,21 +948,6 @@
}
}
- /**
- * Enables this provider. When enabled, calls to getStatus()
- * must be handled. Hardware may be started up
- * when the provider is enabled.
- */
- @Override
- public void enable() {
- synchronized (mLock) {
- if (mEnabled) return;
- mEnabled = true;
- }
-
- sendMessage(ENABLE, 1, null);
- }
-
private void setSuplHostPort(String hostString, String portString) {
if (hostString != null) {
mSuplServerHost = hostString;
@@ -1052,21 +1031,6 @@
}
}
- /**
- * Disables this provider. When disabled, calls to getStatus()
- * need not be handled. Hardware may be shut
- * down while the provider is disabled.
- */
- @Override
- public void disable() {
- synchronized (mLock) {
- if (!mEnabled) return;
- mEnabled = false;
- }
-
- sendMessage(ENABLE, 0, null);
- }
-
private void handleDisable() {
if (DEBUG) Log.d(TAG, "handleDisable");
@@ -1083,7 +1047,6 @@
mGnssNavigationMessageProvider.onGpsEnabledChanged();
}
- @Override
public boolean isEnabled() {
synchronized (mLock) {
return mEnabled;
@@ -1147,7 +1110,7 @@
updateClientUids(mWorkSource);
mFixInterval = (int) mProviderRequest.interval;
- mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+ mLowPowerMode = mProviderRequest.lowPowerMode;
// check for overflow
if (mFixInterval != mProviderRequest.interval) {
Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
@@ -1171,7 +1134,8 @@
// set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
// and our fix interval is not short
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent); }
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+ }
}
} else {
updateClientUids(new WorkSource());
@@ -1220,16 +1184,14 @@
List<WorkChain> goneChains = diffs[1];
if (newChains != null) {
- for (int i = 0; i < newChains.size(); ++i) {
- final WorkChain newChain = newChains.get(i);
+ for (WorkChain newChain : newChains) {
mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
newChain.getAttributionTag());
}
}
if (goneChains != null) {
- for (int i = 0; i < goneChains.size(); i++) {
- final WorkChain goneChain = goneChains.get(i);
+ for (WorkChain goneChain : goneChains) {
mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
goneChain.getAttributionTag());
}
@@ -1262,32 +1224,27 @@
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
+ public void sendExtraCommand(String command, Bundle extras) {
long identity = Binder.clearCallingIdentity();
try {
- boolean result = false;
-
if ("delete_aiding_data".equals(command)) {
- result = deleteAidingData(extras);
+ deleteAidingData(extras);
} else if ("force_time_injection".equals(command)) {
requestUtcTime();
- result = true;
} else if ("force_xtra_injection".equals(command)) {
if (mSupportsXtra) {
xtraDownloadRequest();
- result = true;
}
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
- return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- private boolean deleteAidingData(Bundle extras) {
+ private void deleteAidingData(Bundle extras) {
int flags;
if (extras == null) {
@@ -1311,10 +1268,7 @@
if (flags != 0) {
native_delete_aiding_data(flags);
- return true;
}
-
- return false;
}
private void startNavigating(boolean singleShot) {
@@ -1358,7 +1312,7 @@
}
int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
- mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+ mLowPowerMode = mProviderRequest.lowPowerMode;
if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
interval, 0, 0, mLowPowerMode)) {
mStarted = false;
@@ -1415,10 +1369,7 @@
return ((mEngineCapabilities & capability) != 0);
}
-
- /**
- * called from native code to update our position.
- */
+ @NativeEntryPoint
private void reportLocation(boolean hasLatLong, Location location) {
sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
}
@@ -1444,11 +1395,7 @@
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
location.setExtras(mLocationExtras.getBundle());
- try {
- mILocationManager.reportLocation(location, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
+ reportLocation(location);
if (mStarted) {
mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1473,7 +1420,7 @@
}
// notify status listeners
- mListenerHelper.onFirstFix(mTimeToFirstFix);
+ mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
}
if (mSingleShot) {
@@ -1482,7 +1429,8 @@
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// For devices that use framework scheduling, a timer may be set to ensure we don't
- // spend too much power searching for a location, when the requested update rate is slow.
+ // spend too much power searching for a location, when the requested update rate is
+ // slow.
// As we just recievied a location, we'll cancel that timer.
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
mAlarmManager.cancel(mTimeoutIntent);
@@ -1502,9 +1450,7 @@
}
}
- /**
- * called from native code to update our status
- */
+ @NativeEntryPoint
private void reportStatus(int status) {
if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
@@ -1512,22 +1458,19 @@
switch (status) {
case GPS_STATUS_SESSION_BEGIN:
mNavigating = true;
- mEngineOn = true;
break;
case GPS_STATUS_SESSION_END:
mNavigating = false;
break;
case GPS_STATUS_ENGINE_ON:
- mEngineOn = true;
break;
case GPS_STATUS_ENGINE_OFF:
- mEngineOn = false;
mNavigating = false;
break;
}
if (wasNavigating != mNavigating) {
- mListenerHelper.onStatusChanged(mNavigating);
+ mGnssStatusListenerHelper.onStatusChanged(mNavigating);
// send an intent to notify that the GPS has been enabled or disabled
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -1538,17 +1481,15 @@
// Helper class to carry data to handler for reportSvStatus
private static class SvStatusInfo {
- public int mSvCount;
- public int[] mSvidWithFlags;
- public float[] mCn0s;
- public float[] mSvElevations;
- public float[] mSvAzimuths;
- public float[] mSvCarrierFreqs;
+ private int mSvCount;
+ private int[] mSvidWithFlags;
+ private float[] mCn0s;
+ private float[] mSvElevations;
+ private float[] mSvAzimuths;
+ private float[] mSvCarrierFreqs;
}
- /**
- * called from native code to update SV info
- */
+ @NativeEntryPoint
private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
SvStatusInfo svStatusInfo = new SvStatusInfo();
@@ -1563,7 +1504,7 @@
}
private void handleReportSvStatus(SvStatusInfo info) {
- mListenerHelper.onSvStatusChanged(
+ mGnssStatusListenerHelper.onSvStatusChanged(
info.mSvCount,
info.mSvidWithFlags,
info.mCn0s,
@@ -1622,75 +1563,52 @@
}
}
- /**
- * called from native code to update AGPS status
- */
+ @NativeEntryPoint
private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
mNetworkConnectivityHandler.onReportAGpsStatus(type, status, ipaddr);
}
- /**
- * called from native code to report NMEA data received
- */
+ @NativeEntryPoint
private void reportNmea(long timestamp) {
if (!mItarSpeedLimitExceeded) {
int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
- mListenerHelper.onNmeaReceived(timestamp, nmea);
+ mGnssStatusListenerHelper.onNmeaReceived(timestamp, nmea);
}
}
- /**
- * called from native code - GNSS measurements callback
- */
+ @NativeEntryPoint
private void reportMeasurementData(GnssMeasurementsEvent event) {
if (!mItarSpeedLimitExceeded) {
// send to handler to allow native to return quickly
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGnssMeasurementsProvider.onMeasurementsAvailable(event);
- }
- });
+ mHandler.post(() -> mGnssMeasurementsProvider.onMeasurementsAvailable(event));
}
}
- /**
- * called from native code - GNSS navigation message callback
- */
+ @NativeEntryPoint
private void reportNavigationMessage(GnssNavigationMessage event) {
if (!mItarSpeedLimitExceeded) {
// send to handler to allow native to return quickly
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
- }
- });
+ mHandler.post(() -> mGnssNavigationMessageProvider.onNavigationMessageAvailable(event));
}
}
- /**
- * called from native code to inform us what the GPS engine capabilities are
- */
+ @NativeEntryPoint
private void setEngineCapabilities(final int capabilities) {
// send to handler thread for fast native return, and in-order handling
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mEngineCapabilities = capabilities;
+ mHandler.post(() -> {
+ mEngineCapabilities = capabilities;
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
- mNtpTimeHelper.enablePeriodicTimeInjection();
- requestUtcTime();
- }
-
- mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_MEASUREMENTS));
- mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_NAV_MESSAGES));
- restartRequests();
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+ mNtpTimeHelper.enablePeriodicTimeInjection();
+ requestUtcTime();
}
+
+ mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_MEASUREMENTS));
+ mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_NAV_MESSAGES));
+ restartRequests();
});
}
@@ -1710,27 +1628,21 @@
updateRequirements();
}
- /**
- * Called from native code to inform us the hardware year.
- */
+ @NativeEntryPoint
private void setGnssYearOfHardware(final int yearOfHardware) {
// mHardwareYear is simply set here, to be read elsewhere, and is volatile for safe sync
if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
mHardwareYear = yearOfHardware;
}
- /**
- * Called from native code to inform us the hardware model name.
- */
+ @NativeEntryPoint
private void setGnssHardwareModelName(final String modelName) {
// mHardwareModelName is simply set here, to be read elsewhere, and volatile for safe sync
if (DEBUG) Log.d(TAG, "setGnssModelName called with " + modelName);
mHardwareModelName = modelName;
}
- /**
- * Called from native code to inform us GNSS HAL service died.
- */
+ @NativeEntryPoint
private void reportGnssServiceDied() {
if (DEBUG) Log.d(TAG, "reportGnssServiceDied");
mHandler.post(() -> {
@@ -1750,6 +1662,7 @@
* Returns the year of underlying GPS hardware.
*/
int getGnssYearOfHardware();
+
/**
* Returns the model name of underlying GPS hardware.
*/
@@ -1765,6 +1678,7 @@
public int getGnssYearOfHardware() {
return mHardwareYear;
}
+
@Override
public String getGnssHardwareModelName() {
return mHardwareModelName;
@@ -1790,32 +1704,19 @@
* @hide
*/
public GnssMetricsProvider getGnssMetricsProvider() {
- return new GnssMetricsProvider() {
- @Override
- public String getGnssMetricsAsProtoString() {
- return mGnssMetrics.dumpGnssMetricsAsProtoString();
- }
- };
+ return () -> mGnssMetrics.dumpGnssMetricsAsProtoString();
}
- /**
- * called from native code - GNSS location batch callback
- */
+ @NativeEntryPoint
private void reportLocationBatch(Location[] locationArray) {
List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
if (DEBUG) {
Log.d(TAG, "Location batch of size " + locationArray.length + " reported");
}
- try {
- mILocationManager.reportLocationBatch(locations);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocationBatch");
- }
+ reportLocation(locations);
}
- /**
- * called from native code to request XTRA data
- */
+ @NativeEntryPoint
private void xtraDownloadRequest() {
if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
@@ -1824,7 +1725,7 @@
/**
* Converts the GPS HAL status to the internal Geofence Hardware status.
*/
- private int getGeofenceStatus(int status) {
+ private static int getGeofenceStatus(int status) {
switch (status) {
case GPS_GEOFENCE_OPERATION_SUCCESS:
return GeofenceHardware.GEOFENCE_SUCCESS;
@@ -1843,81 +1744,80 @@
}
}
- /**
- * Called from native to report GPS Geofence transition
- * All geofence callbacks are called on the same thread
- */
+ @NativeEntryPoint
private void reportGeofenceTransition(int geofenceId, Location location, int transition,
long transitionTimestamp) {
- if (mGeofenceHardwareImpl == null) {
- mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
- }
+ mHandler.post(() -> {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
- mGeofenceHardwareImpl.reportGeofenceTransition(
- geofenceId,
- location,
- transition,
- transitionTimestamp,
- GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
- FusedBatchOptions.SourceTechnologies.GNSS);
+ mGeofenceHardwareImpl.reportGeofenceTransition(
+ geofenceId,
+ location,
+ transition,
+ transitionTimestamp,
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ FusedBatchOptions.SourceTechnologies.GNSS);
+ });
}
- /**
- * called from native code to report GPS status change.
- */
+ @NativeEntryPoint
private void reportGeofenceStatus(int status, Location location) {
- if (mGeofenceHardwareImpl == null) {
- mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
- }
- int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
- if (status == GPS_GEOFENCE_AVAILABLE) {
- monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
- }
- mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
- GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
- monitorStatus,
- location,
- FusedBatchOptions.SourceTechnologies.GNSS);
+ mHandler.post(() -> {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+ if (status == GPS_GEOFENCE_AVAILABLE) {
+ monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
+ }
+ mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ monitorStatus,
+ location,
+ FusedBatchOptions.SourceTechnologies.GNSS);
+ });
}
- /**
- * called from native code - Geofence Add callback
- */
+ @NativeEntryPoint
private void reportGeofenceAddStatus(int geofenceId, int status) {
- if (mGeofenceHardwareImpl == null) {
- mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
- }
- mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
+ mHandler.post(() -> {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
+ });
}
- /**
- * called from native code - Geofence Remove callback
- */
+ @NativeEntryPoint
private void reportGeofenceRemoveStatus(int geofenceId, int status) {
- if (mGeofenceHardwareImpl == null) {
- mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
- }
- mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
+ mHandler.post(() -> {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
+ });
}
- /**
- * called from native code - Geofence Pause callback
- */
+ @NativeEntryPoint
private void reportGeofencePauseStatus(int geofenceId, int status) {
- if (mGeofenceHardwareImpl == null) {
- mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
- }
- mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
+ mHandler.post(() -> {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
+ });
}
- /**
- * called from native code - Geofence Resume callback
- */
+ @NativeEntryPoint
private void reportGeofenceResumeStatus(int geofenceId, int status) {
- if (mGeofenceHardwareImpl == null) {
- mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
- }
- mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
+ mHandler.post(() -> {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
+ });
}
//=============================================================
@@ -1942,7 +1842,8 @@
return mNetInitiatedListener;
}
- // Called by JNI function to report an NI request.
+ /** Reports a NI notification. */
+ @NativeEntryPoint
public void reportNiNotification(
int notificationId,
int niType,
@@ -1985,11 +1886,10 @@
}
/**
- * Called from native code to request set id info.
* We should be careful about receiving null string from the TelephonyManager,
* because sending null String to JNI function would cause a crash.
*/
-
+ @NativeEntryPoint
private void requestSetID(int flags) {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2018,9 +1918,7 @@
native_agps_set_id(type, data);
}
- /**
- * Called from native code to request location info.
- */
+ @NativeEntryPoint
private void requestLocation(boolean independentFromGnss) {
if (DEBUG) {
Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss);
@@ -2028,17 +1926,13 @@
sendMessage(REQUEST_LOCATION, 0, independentFromGnss);
}
- /**
- * Called from native code to request utc time info
- */
+ @NativeEntryPoint
private void requestUtcTime() {
if (DEBUG) Log.d(TAG, "utcTimeRequest");
sendMessage(INJECT_NTP_TIME, 0, null);
}
- /**
- * Called from native code to request reference location info
- */
+ @NativeEntryPoint
private void requestRefLocation() {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2092,11 +1986,7 @@
int message = msg.what;
switch (message) {
case ENABLE:
- if (msg.arg1 == 1) {
- handleEnable();
- } else {
- handleDisable();
- }
+ handleEnable();
break;
case SET_REQUEST:
GpsRequest gpsRequest = (GpsRequest) msg.obj;
@@ -2141,7 +2031,8 @@
}
/**
- * This method is bound to {@link #GnssLocationProvider(Context, ILocationManager, Looper)}.
+ * This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager,
+ * Looper)}.
* It is in charge of loading properties and registering for events that will be posted to
* this handler.
*/
@@ -2194,12 +2085,11 @@
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
long minTime = 0;
float minDistance = 0;
- boolean oneShot = false;
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
LocationManager.PASSIVE_PROVIDER,
minTime,
minDistance,
- oneShot);
+ false);
// Don't keep track of this request since it's done on behalf of other clients
// (which are kept track of separately).
request.setHideFromAppOps(true);
@@ -2207,11 +2097,14 @@
request,
new NetworkLocationListener(),
getLooper());
+
+ // enable gps provider, it will never be disabled (legacy behavior)
+ sendEmptyMessage(ENABLE);
}
}
private abstract class LocationChangeListener implements LocationListener {
- int numLocationUpdateRequest;
+ private int mNumLocationUpdateRequest;
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 0add863..3e2ba87 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -38,7 +38,6 @@
private static final String TAG = "GnssMeasurementsProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final Context mContext;
private final GnssMeasurementProviderNative mNative;
private boolean mIsCollectionStarted;
@@ -51,8 +50,7 @@
@VisibleForTesting
GnssMeasurementsProvider(Context context, Handler handler,
GnssMeasurementProviderNative aNative) {
- super(handler, TAG);
- mContext = context;
+ super(context, handler, TAG);
mNative = aNative;
}
@@ -98,9 +96,13 @@
}
public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
- ListenerOperation<IGnssMeasurementsListener> operation =
- listener -> listener.onGnssMeasurementsReceived(event);
- foreach(operation);
+ foreach((IGnssMeasurementsListener listener, int uid, String packageName) -> {
+ if (!hasPermission(uid, packageName)) {
+ logPermissionDisabledEventNotReported(TAG, packageName, "GNSS measurements");
+ return;
+ }
+ listener.onGnssMeasurementsReceived(event);
+ });
}
public void onCapabilitiesUpdated(boolean isGnssMeasurementsSupported) {
@@ -149,7 +151,8 @@
}
@Override
- public void execute(IGnssMeasurementsListener listener) throws RemoteException {
+ public void execute(IGnssMeasurementsListener listener,
+ int uid, String packageName) throws RemoteException {
listener.onStatusChanged(mStatus);
}
}
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index 1b4fd18..679919f 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.content.Context;
import android.location.GnssNavigationMessage;
import android.location.IGnssNavigationMessageListener;
import android.os.Handler;
@@ -39,13 +40,14 @@
private final GnssNavigationMessageProviderNative mNative;
private boolean mCollectionStarted;
- protected GnssNavigationMessageProvider(Handler handler) {
- this(handler, new GnssNavigationMessageProviderNative());
+ protected GnssNavigationMessageProvider(Context context, Handler handler) {
+ this(context, handler, new GnssNavigationMessageProviderNative());
}
@VisibleForTesting
- GnssNavigationMessageProvider(Handler handler, GnssNavigationMessageProviderNative aNative) {
- super(handler, TAG);
+ GnssNavigationMessageProvider(Context context, Handler handler,
+ GnssNavigationMessageProviderNative aNative) {
+ super(context, handler, TAG);
mNative = aNative;
}
@@ -84,15 +86,10 @@
}
public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
- ListenerOperation<IGnssNavigationMessageListener> operation =
- new ListenerOperation<IGnssNavigationMessageListener>() {
- @Override
- public void execute(IGnssNavigationMessageListener listener)
- throws RemoteException {
- listener.onGnssNavigationMessageReceived(event);
- }
- };
- foreach(operation);
+ foreach((IGnssNavigationMessageListener listener, int uid, String packageName) -> {
+ listener.onGnssNavigationMessageReceived(event);
+ }
+ );
}
public void onCapabilitiesUpdated(boolean isGnssNavigationMessageSupported) {
@@ -138,7 +135,8 @@
}
@Override
- public void execute(IGnssNavigationMessageListener listener) throws RemoteException {
+ public void execute(IGnssNavigationMessageListener listener,
+ int uid, String packageName) throws RemoteException {
listener.onStatusChanged(mStatus);
}
}
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 124220f..454dbdd 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -16,16 +16,20 @@
package com.android.server.location;
+import android.content.Context;
import android.location.IGnssStatusListener;
import android.os.Handler;
-import android.os.RemoteException;
+import android.util.Log;
/**
* Implementation of a handler for {@link IGnssStatusListener}.
*/
-abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
- protected GnssStatusListenerHelper(Handler handler) {
- super(handler, "GnssStatusListenerHelper");
+public abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
+ private static final String TAG = "GnssStatusListenerHelper";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ protected GnssStatusListenerHelper(Context context, Handler handler) {
+ super(context, handler, TAG);
setSupported(GnssLocationProvider.isSupported());
}
@@ -43,33 +47,22 @@
}
public void onStatusChanged(boolean isNavigating) {
- Operation operation;
if (isNavigating) {
- operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onGnssStarted();
- }
- };
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ listener.onGnssStarted();
+ });
} else {
- operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onGnssStopped();
- }
- };
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ listener.onGnssStopped();
+ });
}
- foreach(operation);
}
public void onFirstFix(final int timeToFirstFix) {
- Operation operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onFirstFix(timeToFirstFix);
- }
- };
- foreach(operation);
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ listener.onFirstFix(timeToFirstFix);
+ }
+ );
}
public void onSvStatusChanged(
@@ -79,30 +72,23 @@
final float[] elevations,
final float[] azimuths,
final float[] carrierFreqs) {
- Operation operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onSvStatusChanged(
- svCount,
- prnWithFlags,
- cn0s,
- elevations,
- azimuths,
- carrierFreqs);
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ if (!hasPermission(uid, packageName)) {
+ logPermissionDisabledEventNotReported(TAG, packageName, "GNSS status");
+ return;
}
- };
- foreach(operation);
+ listener.onSvStatusChanged(svCount, prnWithFlags, cn0s, elevations, azimuths,
+ carrierFreqs);
+ });
}
public void onNmeaReceived(final long timestamp, final String nmea) {
- Operation operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onNmeaReceived(timestamp, nmea);
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ if (!hasPermission(uid, packageName)) {
+ logPermissionDisabledEventNotReported(TAG, packageName, "NMEA");
+ return;
}
- };
- foreach(operation);
+ listener.onNmeaReceived(timestamp, nmea);
+ });
}
-
- private interface Operation extends ListenerOperation<IGnssStatusListener> {}
}
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
deleted file mode 100644
index 6785964..0000000
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.location;
-
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.os.WorkSource;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Location Manager's interface for location providers.
- * @hide
- */
-public abstract class LocationProviderInterface {
-
- /** Get name. */
- public abstract String getName();
-
- /** Enable. */
- public abstract void enable();
-
- /** Disable. */
- public abstract void disable();
-
- /** Is enabled. */
- public abstract boolean isEnabled();
-
- /** Set request. */
- public abstract void setRequest(ProviderRequest request, WorkSource source);
-
- /** dump. */
- public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
- /** Get properties. */
- public abstract ProviderProperties getProperties();
-
- /**
- * Get status.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public int getStatus(Bundle extras) {
- return LocationProvider.AVAILABLE;
- }
-
- /**
- * Get status update time.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public long getStatusUpdateTime() {
- return 0;
- }
-
- /** Send extra command. */
- public abstract boolean sendExtraCommand(String command, Bundle extras);
-}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index b408414..dfcef70 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
@@ -27,6 +28,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.os.BackgroundThread;
@@ -41,21 +43,35 @@
/**
* Proxy for ILocationProvider implementations.
*/
-public class LocationProviderProxy extends LocationProviderInterface {
+public class LocationProviderProxy extends AbstractLocationProvider {
+
private static final String TAG = "LocationProviderProxy";
private static final boolean D = LocationManagerService.D;
- private final ServiceWatcher mServiceWatcher;
-
- private final String mName;
-
// used to ensure that updates to mRequest and mWorkSource are atomic
private final Object mRequestLock = new Object();
+ private final ServiceWatcher mServiceWatcher;
- private volatile boolean mEnabled = false;
- @Nullable
- private volatile ProviderProperties mProperties;
+ private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
+ // executed on binder thread
+ @Override
+ public void onSetEnabled(boolean enabled) {
+ LocationProviderProxy.this.setEnabled(enabled);
+ }
+
+ // executed on binder thread
+ @Override
+ public void onSetProperties(ProviderProperties properties) {
+ LocationProviderProxy.this.setProperties(properties);
+ }
+
+ // executed on binder thread
+ @Override
+ public void onReportLocation(Location location) {
+ LocationProviderProxy.this.reportLocation(location);
+ }
+ };
@GuardedBy("mRequestLock")
@Nullable
@@ -69,10 +85,10 @@
*/
@Nullable
public static LocationProviderProxy createAndBind(
- Context context, String name, String action,
+ Context context, LocationProviderManager locationProviderManager, String action,
int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
- LocationProviderProxy proxy = new LocationProviderProxy(context, name,
+ LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager,
action, overlaySwitchResId, defaultServicePackageNameResId,
initialPackageNamesResId);
if (proxy.bind()) {
@@ -82,21 +98,27 @@
}
}
- private LocationProviderProxy(Context context, String name,
+ private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
String action, int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
+ super(locationProviderManager, false);
mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
defaultServicePackageNameResId, initialPackageNamesResId,
BackgroundThread.getHandler()) {
+
@Override
protected void onBind() {
runOnBinder(LocationProviderProxy.this::initializeService);
}
- };
- mName = name;
- mProperties = null;
+ @Override
+ protected void onUnbind() {
+ setEnabled(false);
+ setProperties(null);
+ }
+ };
+
mRequest = null;
mWorkSource = new WorkSource();
}
@@ -107,84 +129,27 @@
private void initializeService(IBinder binder) {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- if (D) Log.d(TAG, "applying state to connected service");
-
- ProviderProperties[] properties = new ProviderProperties[1];
- ProviderRequest request;
- WorkSource source;
- synchronized (mRequestLock) {
- request = mRequest;
- source = mWorkSource;
- }
+ if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
try {
- // load properties from provider
- properties[0] = service.getProperties();
- if (properties[0] == null) {
- Log.e(TAG, mServiceWatcher.getCurrentPackageName()
- + " has invalid location provider properties");
- }
+ service.setLocationProviderManager(mManager);
- // apply current state to new service
- if (mEnabled) {
- service.enable();
- if (request != null) {
- service.setRequest(request, source);
+ synchronized (mRequestLock) {
+ if (mRequest != null) {
+ service.setRequest(mRequest, mWorkSource);
}
}
} catch (RemoteException e) {
Log.w(TAG, e);
}
-
- mProperties = properties[0];
}
+ @Nullable
public String getConnectedPackageName() {
return mServiceWatcher.getCurrentPackageName();
}
@Override
- public String getName() {
- return mName;
- }
-
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
-
- @Override
- public void enable() {
- mEnabled = true;
- mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- try {
- service.enable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- });
- }
-
- @Override
- public void disable() {
- mEnabled = false;
- mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- try {
- service.disable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- });
- }
-
- @Override
- public boolean isEnabled() {
- return mEnabled;
- }
-
- @Override
public void setRequest(ProviderRequest request, WorkSource source) {
synchronized (mRequestLock) {
mRequest = request;
@@ -202,60 +167,53 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.append("REMOTE SERVICE");
- pw.append(" name=").append(mName);
- pw.append(" pkg=").append(mServiceWatcher.getCurrentPackageName());
- pw.append(" version=").append(Integer.toString(mServiceWatcher.getCurrentPackageVersion()));
- pw.append('\n');
+ pw.println(" service=" + mServiceWatcher);
mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- TransferPipe.dumpAsync(service.asBinder(), fd, args);
+ TransferPipe.dumpAsync(binder, fd, args);
} catch (IOException | RemoteException e) {
- pw.println("Failed to dump location provider: " + e);
+ pw.println(" failed to dump location provider: " + e);
}
});
}
@Override
public int getStatus(Bundle extras) {
- int[] result = new int[]{LocationProvider.TEMPORARILY_UNAVAILABLE};
+ int[] status = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.getStatus(extras);
+ status[0] = service.getStatus(extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
+ return status[0];
}
@Override
public long getStatusUpdateTime() {
- long[] result = new long[]{0L};
+ long[] updateTime = new long[] {0L};
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.getStatusUpdateTime();
+ updateTime[0] = service.getStatusUpdateTime();
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
+ return updateTime[0];
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- boolean[] result = new boolean[]{false};
+ public void sendExtraCommand(String command, Bundle extras) {
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.sendExtraCommand(command, extras);
+ service.sendExtraCommand(command, extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
}
}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 145aee3..bfbebf7 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -16,14 +16,11 @@
package com.android.server.location;
-import android.location.ILocationManager;
+import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.WorkSource;
-import android.util.Log;
-import android.util.PrintWriterPrinter;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -36,63 +33,57 @@
*
* {@hide}
*/
-public class MockProvider extends LocationProviderInterface {
- private final String mName;
- private final ProviderProperties mProperties;
- private final ILocationManager mLocationManager;
+public class MockProvider extends AbstractLocationProvider {
- private final Location mLocation;
-
- private boolean mHasLocation;
private boolean mEnabled;
-
-
+ @Nullable private Location mLocation;
private int mStatus;
private long mStatusUpdateTime;
private Bundle mExtras;
- private static final String TAG = "MockProvider";
+ public MockProvider(
+ LocationProviderManager locationProviderManager, ProviderProperties properties) {
+ super(locationProviderManager, true);
- public MockProvider(String name, ILocationManager locationManager,
- ProviderProperties properties) {
- if (properties == null) throw new NullPointerException("properties is null");
-
- mName = name;
- mLocationManager = locationManager;
- mProperties = properties;
- mLocation = new Location(name);
-
- mStatus = LocationProvider.AVAILABLE;
- mStatusUpdateTime = 0L;
- mExtras = null;
- }
-
- @Override
- public String getName() {
- return mName;
- }
-
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
-
- @Override
- public void disable() {
- mEnabled = false;
- }
-
- @Override
- public void enable() {
mEnabled = true;
+ mLocation = null;
+ mStatus = LocationProvider.AVAILABLE;
+ mStatusUpdateTime = 0;
+ mExtras = null;
+
+ setProperties(properties);
+ }
+
+ /** Sets the enabled state of this mock provider. */
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ super.setEnabled(enabled);
+ }
+
+ /** Sets the location to report for this mock provider. */
+ public void setLocation(Location l) {
+ mLocation = new Location(l);
+ if (mEnabled) {
+ reportLocation(l);
+ }
+ }
+
+ /** Sets the status for this mock provider. */
+ public void setStatus(int status, Bundle extras, long updateTime) {
+ mStatus = status;
+ mStatusUpdateTime = updateTime;
+ mExtras = extras;
}
@Override
- public boolean isEnabled() {
- return mEnabled;
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(" last location=" + mLocation);
}
@Override
+ public void setRequest(ProviderRequest request, WorkSource source) {}
+
+ @Override
public int getStatus(Bundle extras) {
if (mExtras != null) {
extras.clear();
@@ -107,50 +98,6 @@
return mStatusUpdateTime;
}
- public void setLocation(Location l) {
- mLocation.set(l);
- mHasLocation = true;
- if (mEnabled) {
- try {
- mLocationManager.reportLocation(mLocation, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
- }
- }
-
- public void clearLocation() {
- mHasLocation = false;
- }
-
- /**
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public void setStatus(int status, Bundle extras, long updateTime) {
- mStatus = status;
- mStatusUpdateTime = updateTime;
- mExtras = extras;
- }
-
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- dump(pw, "");
- }
-
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + mName);
- pw.println(prefix + "mHasLocation=" + mHasLocation);
- pw.println(prefix + "mLocation:");
- mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- pw.println(prefix + "mExtras=" + mExtras);
- }
-
- @Override
- public void setRequest(ProviderRequest request, WorkSource source) { }
-
- @Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
+ public void sendExtraCommand(String command, Bundle extras) {}
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 99c9214..70d64b0 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -17,13 +17,9 @@
package com.android.server.location;
import android.location.Criteria;
-import android.location.ILocationManager;
import android.location.Location;
-import android.location.LocationManager;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.WorkSource;
-import android.util.Log;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -38,41 +34,20 @@
*
* {@hide}
*/
-public class PassiveProvider extends LocationProviderInterface {
- private static final String TAG = "PassiveProvider";
+public class PassiveProvider extends AbstractLocationProvider {
private static final ProviderProperties PROPERTIES = new ProviderProperties(
false, false, false, false, false, false, false,
Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
- private final ILocationManager mLocationManager;
private boolean mReportLocation;
- public PassiveProvider(ILocationManager locationManager) {
- mLocationManager = locationManager;
- }
+ public PassiveProvider(LocationProviderManager locationProviderManager) {
+ super(locationProviderManager, true);
- @Override
- public String getName() {
- return LocationManager.PASSIVE_PROVIDER;
- }
+ mReportLocation = false;
- @Override
- public ProviderProperties getProperties() {
- return PROPERTIES;
- }
-
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- @Override
- public void enable() {
- }
-
- @Override
- public void disable() {
+ setProperties(PROPERTIES);
}
@Override
@@ -82,22 +57,15 @@
public void updateLocation(Location location) {
if (mReportLocation) {
- try {
- // pass the location back to the location manager
- mLocationManager.reportLocation(location, true);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
+ reportLocation(location);
}
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
+ public void sendExtraCommand(String command, Bundle extras) {}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("mReportLocation=" + mReportLocation);
+ pw.println(" report location=" + mReportLocation);
}
}
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index fcdb9d1..37d43fc 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -17,6 +17,8 @@
package com.android.server.location;
import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
@@ -46,6 +48,9 @@
private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
+ protected final Context mContext;
+ protected final AppOpsManager mAppOps;
+
private volatile boolean mIsRegistered; // must access only on handler thread, or read-only
private boolean mHasIsSupported;
@@ -53,10 +58,12 @@
private int mLastReportedResult = RESULT_UNKNOWN;
- protected RemoteListenerHelper(Handler handler, String name) {
+ protected RemoteListenerHelper(Context context, Handler handler, String name) {
Preconditions.checkNotNull(name);
mHandler = handler;
mTag = name;
+ mContext = context;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
// read-only access for a dump() thread assured via volatile
@@ -64,10 +71,10 @@
return mIsRegistered;
}
- public boolean addListener(@NonNull TListener listener) {
+ public boolean addListener(@NonNull TListener listener, int uid, String packageName) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
IBinder binder = listener.asBinder();
- LinkedListener deathListener = new LinkedListener(listener);
+ LinkedListener deathListener = new LinkedListener(listener, uid, packageName);
synchronized (mListenerMap) {
if (mListenerMap.containsKey(binder)) {
// listener already added
@@ -102,7 +109,7 @@
// asynchronously in the future
return true;
}
- post(listener, getHandlerOperation(result));
+ post(deathListener, getHandlerOperation(result));
}
return true;
}
@@ -130,7 +137,7 @@
protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
protected interface ListenerOperation<TListener extends IInterface> {
- void execute(TListener listener) throws RemoteException;
+ void execute(TListener listener, int uid, String packageName) throws RemoteException;
}
protected void foreach(ListenerOperation<TListener> operation) {
@@ -170,15 +177,28 @@
}
}
- private void foreachUnsafe(ListenerOperation<TListener> operation) {
- for (LinkedListener linkedListener : mListenerMap.values()) {
- post(linkedListener.getUnderlyingListener(), operation);
+ protected boolean hasPermission(int uid, String packageName) {
+ return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ protected void logPermissionDisabledEventNotReported(String tag, String packageName,
+ String event) {
+ if (Log.isLoggable(tag, Log.DEBUG)) {
+ Log.d(tag, "Location permission disabled. Skipping " + event + " reporting for app: "
+ + packageName);
}
}
- private void post(TListener listener, ListenerOperation<TListener> operation) {
+ private void foreachUnsafe(ListenerOperation<TListener> operation) {
+ for (LinkedListener linkedListener : mListenerMap.values()) {
+ post(linkedListener, operation);
+ }
+ }
+
+ private void post(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
if (operation != null) {
- mHandler.post(new HandlerRunnable(listener, operation));
+ mHandler.post(new HandlerRunnable(linkedListener, operation));
}
}
@@ -193,13 +213,9 @@
}
if (!mIsRegistered) {
// post back a failure
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mListenerMap) {
- ListenerOperation<TListener> operation = getHandlerOperation(registrationState);
- foreachUnsafe(operation);
- }
+ mHandler.post(() -> {
+ synchronized (mListenerMap) {
+ foreachUnsafe(getHandlerOperation(registrationState));
}
});
}
@@ -208,16 +224,14 @@
}
private void tryUnregister() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (!mIsRegistered) {
- return;
+ mHandler.post(() -> {
+ if (!mIsRegistered) {
+ return;
+ }
+ unregisterFromService();
+ mIsRegistered = false;
}
- unregisterFromService();
- mIsRegistered = false;
- }
- });
+ );
}
private int calculateCurrentResultUnsafe() {
@@ -240,14 +254,13 @@
private class LinkedListener implements IBinder.DeathRecipient {
private final TListener mListener;
+ private final int mUid;
+ private final String mPackageName;
- public LinkedListener(@NonNull TListener listener) {
+ LinkedListener(@NonNull TListener listener, int uid, String packageName) {
mListener = listener;
- }
-
- @NonNull
- public TListener getUnderlyingListener() {
- return mListener;
+ mUid = uid;
+ mPackageName = packageName;
}
@Override
@@ -258,18 +271,19 @@
}
private class HandlerRunnable implements Runnable {
- private final TListener mListener;
+ private final LinkedListener mLinkedListener;
private final ListenerOperation<TListener> mOperation;
- public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
- mListener = listener;
+ HandlerRunnable(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
+ mLinkedListener = linkedListener;
mOperation = operation;
}
@Override
public void run() {
try {
- mOperation.execute(mListener);
+ mOperation.execute(mLinkedListener.mListener, mLinkedListener.mUid,
+ mLinkedListener.mPackageName);
} catch (RemoteException e) {
Log.v(mTag, "Error in monitored listener.", e);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index c4f1f3d..4480435 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -24,8 +24,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.biometrics.BiometricManager;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteCallbackList;
@@ -62,7 +61,7 @@
private final Context mContext;
private AlarmManager mAlarmManager;
- private FingerprintManager mFingerprintManager;
+ private BiometricManager mBiometricManager;
public LockSettingsStrongAuth(Context context) {
mContext = context;
@@ -71,9 +70,8 @@
}
public void systemReady() {
- final PackageManager pm = mContext.getPackageManager();
- if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ if (BiometricManager.hasBiometrics(mContext)) {
+ mBiometricManager = mContext.getSystemService(BiometricManager.class);
}
}
@@ -187,9 +185,9 @@
}
public void reportSuccessfulStrongAuthUnlock(int userId) {
- if (mFingerprintManager != null) {
- byte[] token = null; /* TODO: pass real auth token once fp HAL supports it */
- mFingerprintManager.resetTimeout(token);
+ if (mBiometricManager != null) {
+ byte[] token = null; /* TODO: pass real auth token once HAL supports it */
+ mBiometricManager.resetTimeout(token);
}
final int argNotUsed = 0;
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d32c299..0e195bc 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.os.UserManager;
import android.security.GateKeeper;
+import android.security.Scrypt;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.util.ArrayMap;
@@ -1173,11 +1174,10 @@
}
protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
- return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
+ return new Scrypt().scrypt(password.getBytes(), salt, N, r, p, outLen);
}
native long nativeSidFromPasswordHandle(byte[] handle);
- native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
protected static ArrayList<Byte> toByteArrayList(byte[] data) {
ArrayList<Byte> result = new ArrayList<Byte>(data.length);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
index 6e08949..26e8270 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -16,13 +16,17 @@
package com.android.server.locksettings.recoverablekeystore.certificate;
-import static javax.xml.xpath.XPathConstants.NODESET;
-
import android.annotation.IntDef;
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -40,7 +44,6 @@
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
-import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
@@ -58,15 +61,6 @@
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
/** Utility functions related to parsing and validating public-key certificates. */
public final class CertUtils {
@@ -167,50 +161,63 @@
static List<String> getXmlNodeContents(@MustExist int mustExist, Element rootNode,
String... nodeTags)
throws CertParsingException {
- String expression = String.join("/", nodeTags);
-
- XPath xPath = XPathFactory.newInstance().newXPath();
- NodeList nodeList;
- try {
- nodeList = (NodeList) xPath.compile(expression).evaluate(rootNode, NODESET);
- } catch (XPathExpressionException e) {
- throw new CertParsingException(e);
+ if (nodeTags.length == 0) {
+ throw new CertParsingException("The tag list must not be empty");
}
- switch (mustExist) {
- case MUST_EXIST_UNENFORCED:
- break;
-
- case MUST_EXIST_EXACTLY_ONE:
- if (nodeList.getLength() != 1) {
- throw new CertParsingException(
- "The XML file must contain exactly one node with the path "
- + expression);
- }
- break;
-
- case MUST_EXIST_AT_LEAST_ONE:
- if (nodeList.getLength() == 0) {
- throw new CertParsingException(
- "The XML file must contain at least one node with the path "
- + expression);
- }
- break;
-
- default:
- throw new UnsupportedOperationException(
- "This value of MustExist is not supported: " + mustExist);
+ // Go down through all the intermediate node tags (except the last tag for the leaf nodes).
+ // Note that this implementation requires that at most one path exists for the given
+ // intermediate node tags.
+ Element parent = rootNode;
+ for (int i = 0; i < nodeTags.length - 1; i++) {
+ String tag = nodeTags[i];
+ List<Element> children = getXmlDirectChildren(parent, tag);
+ if ((children.size() == 0 && mustExist != MUST_EXIST_UNENFORCED)
+ || children.size() > 1) {
+ throw new CertParsingException(
+ "The XML file must contain exactly one path with the tag " + tag);
+ }
+ if (children.size() == 0) {
+ return new ArrayList<>();
+ }
+ parent = children.get(0);
}
+ // Then collect the contents of the leaf nodes.
+ List<Element> leafs = getXmlDirectChildren(parent, nodeTags[nodeTags.length - 1]);
+ if (mustExist == MUST_EXIST_EXACTLY_ONE && leafs.size() != 1) {
+ throw new CertParsingException(
+ "The XML file must contain exactly one node with the path "
+ + String.join("/", nodeTags));
+ }
+ if (mustExist == MUST_EXIST_AT_LEAST_ONE && leafs.size() == 0) {
+ throw new CertParsingException(
+ "The XML file must contain at least one node with the path "
+ + String.join("/", nodeTags));
+ }
List<String> result = new ArrayList<>();
- for (int i = 0; i < nodeList.getLength(); i++) {
- Node node = nodeList.item(i);
+ for (Element leaf : leafs) {
// Remove whitespaces and newlines.
- result.add(node.getTextContent().replaceAll("\\s", ""));
+ result.add(leaf.getTextContent().replaceAll("\\s", ""));
}
return result;
}
+ /** Get the direct child nodes with a given tag. */
+ private static List<Element> getXmlDirectChildren(Element parent, String tag) {
+ // Cannot use Element.getElementsByTagName because it will return all descendant elements
+ // with the tag name, i.e. not only the direct child nodes.
+ List<Element> children = new ArrayList<>();
+ NodeList childNodes = parent.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node node = childNodes.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(tag)) {
+ children.add((Element) node);
+ }
+ }
+ return children;
+ }
+
/**
* Decodes a base64-encoded string.
*
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index a16dcf3..a2e7e0c 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -352,7 +352,7 @@
// Clear UID from current stats snapshot
if (mLastSnapshot != null) {
- mLastSnapshot = mLastSnapshot.withoutUids(uids);
+ mLastSnapshot.removeUids(uids);
}
final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 2584187..9b9f4de 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -15,12 +15,15 @@
*/
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.provider.Settings;
import android.telecom.TelecomManager;
import com.android.internal.util.NotificationMessagingUtil;
@@ -47,6 +50,21 @@
@Override
public int compare(NotificationRecord left, NotificationRecord right) {
+ final int leftImportance = left.getImportance();
+ final int rightImportance = right.getImportance();
+ final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
+ final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;
+
+ // With new interruption model, prefer importance bucket above all other criteria
+ // (to ensure buckets are contiguous)
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
+ if (isLeftHighImportance != isRightHighImportance) {
+ // by importance bucket, high importance higher than low importance
+ return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
+ }
+ }
+
// first all colorized notifications
boolean leftImportantColorized = isImportantColorized(left);
boolean rightImportantColorized = isImportantColorized(right);
@@ -86,8 +104,6 @@
return -1 * Boolean.compare(leftPeople, rightPeople);
}
- final int leftImportance = left.getImportance();
- final int rightImportance = right.getImportance();
if (leftImportance != rightImportance) {
// by importance, high to low
return -1 * Integer.compare(leftImportance, rightImportance);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6c2549e..fc364c0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -428,6 +428,7 @@
private GroupHelper mGroupHelper;
private int mAutoGroupAtCount;
private boolean mIsTelevision;
+ private boolean mIsAutomotive;
private MetricsLogger mMetricsLogger;
private Predicate<String> mAllowedManagedServicePackages;
@@ -603,7 +604,8 @@
}
}
- private void handleSavePolicyFile() {
+ @VisibleForTesting
+ protected void handleSavePolicyFile() {
IoThread.getHandler().post(() -> {
if (DBG) Slog.d(TAG, "handleSavePolicyFile");
synchronized (mPolicyFile) {
@@ -1134,6 +1136,7 @@
}
mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
+ handleSavePolicyFile();
}
}
};
@@ -1388,6 +1391,11 @@
}
@VisibleForTesting
+ void setIsAutomotive(boolean isAutomotive) {
+ mIsAutomotive = isAutomotive;
+ }
+
+ @VisibleForTesting
void setIsTelevision(boolean isTelevision) {
mIsTelevision = isTelevision;
}
@@ -1543,6 +1551,9 @@
mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
|| mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
+
+ mIsAutomotive =
+ mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
}
@Override
@@ -3610,12 +3621,19 @@
&& mAssistants.isSameUser(token, r.getUserId())) {
applyAdjustment(r, adjustment);
r.applyAdjustments();
+ // importance is checked at the beginning of the
+ // PostNotificationRunnable, before the signal extractors are run, so
+ // calculate the final importance here
+ r.calculateImportance();
foundEnqueued = true;
break;
}
}
if (!foundEnqueued) {
// adjustment arrived too late to apply to enqueued; apply to posted
+ // However, since the notification is now posted and may have alerted,
+ // ignore any importance related adjustments
+ adjustment.getSignals().remove(Adjustment.KEY_IMPORTANCE);
applyAdjustmentFromAssistant(token, adjustment);
}
}
@@ -3645,8 +3663,12 @@
NotificationRecord r = mNotificationsByKey.get(adjustment.getKey());
if (r != null && mAssistants.isSameUser(token, r.getUserId())) {
applyAdjustment(r, adjustment);
- r.applyImportanceFromAdjustments();
- if (r.getImportance() == IMPORTANCE_NONE) {
+ // If the assistant has blocked the notification, cancel it
+ // This will trigger a sort, so we don't have to explicitly ask for
+ // one here.
+ if (adjustment.getSignals().containsKey(Adjustment.KEY_IMPORTANCE)
+ && adjustment.getSignals().getInt(Adjustment.KEY_IMPORTANCE)
+ == IMPORTANCE_NONE) {
cancelNotificationsFromListener(token, new String[]{r.getKey()});
} else {
needsSort = true;
@@ -4747,7 +4769,6 @@
enqueueStatus);
}
- mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mAssistants.isEnabled()) {
mAssistants.onNotificationEnqueuedLocked(r);
@@ -4831,7 +4852,7 @@
| Notification.FLAG_NO_CLEAR;
}
- applyZenModeLocked(r);
+ mRankingHelper.extractSignals(r);
mRankingHelper.sort(mNotificationList);
if (!r.isHidden()) {
@@ -5131,7 +5152,9 @@
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold =
- record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
+ mIsAutomotive
+ ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
+ : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
// Remember if this notification already owns the notification channels.
boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
@@ -5495,7 +5518,12 @@
{
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r);
- long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
+ int delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
+ // Accessibility users may need longer timeout duration. This api compares original delay
+ // with user's preference and return longer one. It returns original delay if there's no
+ // preference.
+ delay = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+ AccessibilityManager.FLAG_CONTENT_TEXT);
mHandler.sendMessageDelayed(m, delay);
}
@@ -5609,6 +5637,7 @@
ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
+ int[] importancesBefore = new int[N];
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
@@ -5622,6 +5651,7 @@
suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
smartRepliesBefore.add(r.getSmartReplies());
+ importancesBefore[i] = r.getImportance();
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
@@ -5639,7 +5669,8 @@
r.getSuppressedVisualEffects())
|| !Objects.equals(systemSmartActionsBefore.get(i),
r.getSystemGeneratedSmartActions())
- || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
+ || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())
+ || importancesBefore[i] != r.getImportance()) {
mHandler.scheduleSendRankingUpdate();
return;
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1a9257c..50810cc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -21,10 +21,8 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_POSITIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -609,6 +607,17 @@
mIsAppImportanceLocked, this.sbn.getNotification());
}
+ public boolean hasAdjustment(String key) {
+ synchronized (mAdjustments) {
+ for (Adjustment adjustment : mAdjustments) {
+ if (adjustment.getSignals().containsKey(key)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public void addAdjustment(Adjustment adjustment) {
synchronized (mAdjustments) {
mAdjustments.add(adjustment);
@@ -669,18 +678,6 @@
.addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
getSmartReplies().size()));
}
- }
- applyImportanceFromAdjustments();
- }
- }
-
- /**
- * Update importance from the adjustment.
- */
- public void applyImportanceFromAdjustments() {
- synchronized (mAdjustments) {
- for (Adjustment adjustment : mAdjustments) {
- Bundle signals = adjustment.getSignals();
if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
@@ -752,6 +749,8 @@
*/
public void setSystemImportance(int importance) {
mSystemImportance = importance;
+ // System importance is only changed in enqueue, so it's ok for us to calculate the
+ // importance directly instead of waiting for signal extractor.
calculateImportance();
}
@@ -762,7 +761,16 @@
*/
public void setAssistantImportance(int importance) {
mAssistantImportance = importance;
- calculateImportance();
+ // Unlike the system importance, the assistant importance can change on posted
+ // notifications, so don't calculateImportance() here, but wait for the signal extractors.
+ }
+
+ /**
+ * Returns the importance set by the assistant, or IMPORTANCE_UNSPECIFIED if the assistant
+ * hasn't set it.
+ */
+ public int getAssistantImportance() {
+ return mAssistantImportance;
}
/**
@@ -774,7 +782,8 @@
if (getChannel().hasUserSetImportance()) {
mImportanceExplanation = "user";
}
- if (!getChannel().hasUserSetImportance() && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+ if (!getChannel().hasUserSetImportance()
+ && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
mImportance = mAssistantImportance;
mImportanceExplanation = "asst";
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 65ccecd..7ae2271 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
@@ -77,18 +78,20 @@
IApplicationThread caller,
String callingPackage,
ComponentName component,
- UserHandle user) throws RemoteException {
+ @UserIdInt int userId,
+ boolean launchMainActivity) throws RemoteException {
Preconditions.checkNotNull(callingPackage);
Preconditions.checkNotNull(component);
- Preconditions.checkNotNull(user);
verifyCallingPackage(callingPackage);
+ final int callerUserId = mInjector.getCallingUserId();
+ final int callingUid = mInjector.getCallingUid();
+
List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
- callingPackage, mInjector.getCallingUserId());
- if (!allowedTargetUsers.contains(user)) {
- throw new SecurityException(
- callingPackage + " cannot access unrelated user " + user.getIdentifier());
+ callingPackage, callerUserId);
+ if (!allowedTargetUsers.contains(UserHandle.of(userId))) {
+ throw new SecurityException(callingPackage + " cannot access unrelated user " + userId);
}
// Verify that caller package is starting activity in its own package.
@@ -98,25 +101,43 @@
+ component.getPackageName());
}
- final int callingUid = mInjector.getCallingUid();
-
- // Verify that target activity does handle the intent with ACTION_MAIN and
- // CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present.
- final Intent launchIntent = new Intent(Intent.ACTION_MAIN);
- launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- // Only package name is set here, as opposed to component name, because intent action and
- // category are ignored if component name is present while we are resolving intent.
- launchIntent.setPackage(component.getPackageName());
- verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, user);
+ // Verify that target activity does handle the intent correctly.
+ final Intent launchIntent = new Intent();
+ if (launchMainActivity) {
+ launchIntent.setAction(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ // Only package name is set here, as opposed to component name, because intent action
+ // and category are ignored if component name is present while we are resolving intent.
+ launchIntent.setPackage(component.getPackageName());
+ } else {
+ // If the main activity is not being launched and the users are different, the caller
+ // must have the required permission and the users must be in the same profile group
+ // in order to launch any of its own activities.
+ if (callerUserId != userId) {
+ final int permissionFlag = ActivityManager.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
+ -1, true);
+ if (permissionFlag != PackageManager.PERMISSION_GRANTED
+ || !mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)) {
+ throw new SecurityException("Attempt to launch activity without required "
+ + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
+ + " or target user is not in the same profile group.");
+ }
+ }
+ launchIntent.setComponent(component);
+ }
+ verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, userId);
launchIntent.setPackage(null);
launchIntent.setComponent(component);
mInjector.getActivityTaskManagerInternal().startActivityAsUser(
caller, callingPackage, launchIntent,
- ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(),
- user.getIdentifier());
+ launchMainActivity
+ ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
+ : null,
+ userId);
}
private List<UserHandle> getTargetUserProfilesUnchecked(
@@ -163,7 +184,7 @@
* activity is exported.
*/
private void verifyActivityCanHandleIntentAndExported(
- Intent launchIntent, ComponentName component, int callingUid, UserHandle user) {
+ Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) {
final long ident = mInjector.clearCallingIdentity();
try {
final List<ResolveInfo> apps =
@@ -171,7 +192,7 @@
launchIntent,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
callingUid,
- user.getIdentifier());
+ userId);
final int size = apps.size();
for (int i = 0; i < size; ++i) {
final ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2e9a71a..a95e730 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -27,7 +27,6 @@
import android.app.NotificationManager;
import android.app.PackageDeleteObserver;
import android.app.PackageInstallObserver;
-import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -164,6 +163,11 @@
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
+ // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
+ // shouldn't be needed at all.
+ @GuardedBy("mStagedSessions")
+ private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
+
/** Historical sessions kept around for debugging purposes */
@GuardedBy("mSessions")
private final List<String> mHistoricalSessions = new ArrayList<>();
@@ -481,10 +485,12 @@
if (!PackageHelper.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
- } else {
+ } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else {
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
@@ -536,6 +542,11 @@
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
+ if (params.isStaged) {
+ synchronized (mStagedSessions) {
+ mStagedSessions.put(sessionId, session);
+ }
+ }
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
writeSessionsAsync();
@@ -666,6 +677,18 @@
}
@Override
+ public ParceledListSlice<SessionInfo> getStagedSessions() {
+ final List<SessionInfo> result = new ArrayList<>();
+ synchronized (mStagedSessions) {
+ for (int i = 0; i < mStagedSessions.size(); i++) {
+ final PackageInstallerSession session = mStagedSessions.valueAt(i);
+ result.add(session.generateInfo(false));
+ }
+ }
+ return new ParceledListSlice<>(result);
+ }
+
+ @Override
public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
mPermissionManager.enforceCrossUserPermission(
Binder.getCallingUid(), userId, true, false, "getAllSessions");
@@ -714,22 +737,19 @@
// Check whether the caller is device owner or affiliated profile owner, in which case we do
// it silently.
- final int callingUserId = UserHandle.getUserId(callingUid);
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
- final boolean isDeviceOwnerOrAffiliatedProfileOwner =
- dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)
- && dpmi.isUserAffiliatedWithDevice(callingUserId);
+ final boolean canSilentlyInstallPackage =
+ dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(),
- isDeviceOwnerOrAffiliatedProfileOwner, userId);
+ canSilentlyInstallPackage, userId);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
- } else if (isDeviceOwnerOrAffiliatedProfileOwner) {
+ } else if (canSilentlyInstallPackage) {
// Allow the device owner and affiliated profile owner to silently delete packages
// Need to clear the calling identity to get DELETE_PACKAGES permission
long ident = Binder.clearCallingIdentity();
@@ -1110,6 +1130,18 @@
mInstallHandler.post(new Runnable() {
@Override
public void run() {
+ // TODO: remove this mock implementation.
+ if (session.isStaged()) {
+ // If the session is aborted, don't keep it in memory. Only store
+ // sessions successfully staged.
+ if (!success) {
+ synchronized (mStagedSessions) {
+ mStagedSessions.remove(session.sessionId);
+ }
+ } else {
+ return;
+ }
+ }
synchronized (mSessions) {
mSessions.remove(session.sessionId);
addHistoricalSessionLocked(session);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ea190a7..206a88b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -44,7 +44,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.IApexService;
-import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -125,7 +124,7 @@
import java.util.concurrent.atomic.AtomicInteger;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
- private static final String TAG = "PackageInstaller";
+ private static final String TAG = "PackageInstallerSession";
private static final boolean LOGD = true;
private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
@@ -147,6 +146,7 @@
private static final String ATTR_SEALED = "sealed";
private static final String ATTR_MULTI_PACKAGE = "multiPackage";
private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
+ private static final String ATTR_STAGED_SESSION = "stagedSession";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
private static final String ATTR_INSTALL_LOCATION = "installLocation";
@@ -193,7 +193,7 @@
/** Package of the owner of the installer session */
@GuardedBy("mLock")
- private String mInstallerPackageName;
+ private @Nullable String mInstallerPackageName;
/** Uid of the owner of the installer session */
@GuardedBy("mLock")
@@ -339,11 +339,12 @@
*/
@GuardedBy("mLock")
private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
+ if (userId != UserHandle.getUserId(mInstallerUid)) {
+ return false;
+ }
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
- return dpmi != null && dpmi.isActiveAdminWithPolicy(mInstallerUid,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) && dpmi.isUserAffiliatedWithDevice(
- userId);
+ return dpmi != null && dpmi.canSilentlyInstallPackage(mInstallerPackageName, mInstallerUid);
}
/**
@@ -463,6 +464,7 @@
info.grantedRuntimePermissions = params.grantedRuntimePermissions;
info.installFlags = params.installFlags;
info.isMultiPackage = params.isMultiPackage;
+ info.isStaged = params.isStaged;
info.parentSessionId = mParentSessionId;
info.childSessionIds = mChildSessionIds.copyKeys();
if (info.childSessionIds == null) {
@@ -1047,6 +1049,11 @@
if (committingSession == null) {
return;
}
+ if (isStaged()) {
+ // STOPSHIP: implement staged sessions
+ dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
+ return;
+ }
if (isMultiPackage()) {
final int[] childSessionIds = getChildSessionIds();
List<PackageManagerService.ActiveInstallSession> childSessions =
@@ -1816,6 +1823,11 @@
}
@Override
+ public boolean isStaged() {
+ return params.isStaged;
+ }
+
+ @Override
public int[] getChildSessionIds() {
final int[] childSessionIds = mChildSessionIds.copyKeys();
if (childSessionIds != null) {
@@ -1838,6 +1850,8 @@
return;
}
session.setParentSessionId(this.sessionId);
+ // TODO: sanity check, if parent session is staged then child session should be
+ // marked as staged.
addChildSessionIdInternal(sessionId);
}
}
@@ -1975,6 +1989,7 @@
pw.printPair("mFinalStatus", mFinalStatus);
pw.printPair("mFinalMessage", mFinalMessage);
pw.printPair("params.isMultiPackage", params.isMultiPackage);
+ pw.printPair("params.isStaged", params.isStaged);
pw.println();
pw.decreaseIndent();
@@ -2026,6 +2041,7 @@
writeBooleanAttribute(out, ATTR_SEALED, isSealed());
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
+ writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
// TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
// we've read all sessions.
writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId);
@@ -2140,6 +2156,7 @@
final SessionParams params = new SessionParams(
SessionParams.MODE_INVALID);
params.isMultiPackage = readBooleanAttribute(in, ATTR_MULTI_PACKAGE, false);
+ params.isStaged = readBooleanAttribute(in, ATTR_STAGED_SESSION, false);
params.mode = readIntAttribute(in, ATTR_MODE);
params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8abb500..3e9100e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -58,6 +58,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -85,11 +86,6 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageParser.isApkFile;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
@@ -241,6 +237,7 @@
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.provider.MediaStore;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
@@ -1300,6 +1297,8 @@
final @Nullable String mSetupWizardPackage;
final @Nullable String mStorageManagerPackage;
final @Nullable String mSystemTextClassifierPackage;
+ final @Nullable String mWellbeingPackage;
+ final @Nullable String mDocumenterPackage;
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
@@ -2092,28 +2091,6 @@
}
}
- @GuardedBy("mPackages")
- private void setupBuiltinSharedLibraryDependenciesLocked() {
- // Builtin libraries don't have versions.
- long version = SharedLibraryInfo.VERSION_UNDEFINED;
-
- SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ANDROID_HIDL_MANAGER, version);
- if (libraryInfo != null) {
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_HIDL_BASE, version));
- }
-
- libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_RUNNER, version);
- if (libraryInfo != null) {
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version));
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
- }
-
- libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version);
- if (libraryInfo != null) {
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
- }
- }
-
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
@@ -2221,17 +2198,32 @@
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mInstantAppRegistry = new InstantAppRegistry(this);
- ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
+ ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
+ = systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
- String path = libConfig.valueAt(i);
- addSharedLibraryLPw(path, null, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
- SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
+ SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
+ addSharedLibraryLPw(entry.filename, null, null, name,
+ SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
+ PLATFORM_PACKAGE_NAME, 0);
}
- // Builtin libraries cannot encode their dependency where they are
- // defined, so fix that now.
- setupBuiltinSharedLibraryDependenciesLocked();
+
+ // Now that we have added all the libraries, iterate again to add dependency
+ // information IFF their dependencies are added.
+ long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
+ for (int i = 0; i < builtInLibCount; i++) {
+ String name = libConfig.keyAt(i);
+ SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
+ final int dependencyCount = entry.dependencies.length;
+ for (int j = 0; j < dependencyCount; j++) {
+ final SharedLibraryInfo dependency =
+ getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion);
+ if (dependency != null) {
+ getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency);
+ }
+ }
+ }
SELinuxMMAC.readInstallPolicy();
@@ -2789,6 +2781,9 @@
mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
+ mWellbeingPackage = getWellbeingPackageName();
+ mDocumenterPackage = getDocumenterPackageName();
+
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLPw(null);
@@ -8630,16 +8625,8 @@
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
// The version of the application on the /system partition is less than or
- // equal to the version on the /data partition. Even though the disabled system package
- // is likely to be replaced by a version on the /data partition, we make assumptions
- // that it's part of the mPackages collection during package manager initialization. So,
- // add it to mPackages if there isn't already a package in the collection and then throw
- // an exception to use the application already installed on the /data partition.
- synchronized (mPackages) {
- if (!mPackages.containsKey(pkg.packageName)) {
- mPackages.put(pkg.packageName, pkg);
- }
- }
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at "
+ pkg.codePath + " ignored: updated version " + pkgSetting.versionCode
+ " better than this " + pkg.getLongVersionCode());
@@ -10299,12 +10286,21 @@
compareSignatures(
signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
- // Treat mismatched signatures on system packages using a shared UID as
- // fatal for the system overall, rather than just failing to install
- // whichever package happened to be scanned later.
- throw new IllegalStateException(
- "Signature mismatch on system package " + pkg.packageName
- + " for shared user " + pkgSetting.sharedUser);
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
+ // Mismatched signatures is an error and silently skipping system
+ // packages will likely break the device in unforeseen ways. However,
+ // we allow the device to boot anyway because, prior to P, vendors were
+ // not expecting the platform to crash in this situation.
+ throw new PackageManagerException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: " + pkgSetting.sharedUser);
+ } else {
+ // Treat mismatched signatures on system packages using a shared UID as
+ // fatal for the system overall, rather than just failing to install
+ // whichever package happened to be scanned later.
+ throw new IllegalStateException("Signature mismatch on system package "
+ + pkg.packageName + " for shared user " + pkgSetting.sharedUser);
+ }
}
signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
@@ -11256,7 +11252,7 @@
== UsesPermissionInfo.USAGE_UNDEFINED
|| upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) {
// STOPSHIP: Make this throw
- Slog.wtf(TAG, "Package " + pkg.packageName + " does not provide usage "
+ Slog.e(TAG, "Package " + pkg.packageName + " does not provide usage "
+ "information for permission " + upi.getPermission()
+ ". This will be a fatal error in Q.");
}
@@ -12834,16 +12830,17 @@
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
final long callingId = Binder.clearCallingIdentity();
try {
- synchronized (mPackages) {
- for (int i = 0; i < packageNames.length; i++) {
- final String packageName = packageNames[i];
- if (callingPackage.equals(packageName)) {
- Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
- + (suspended ? "" : "un") + "suspend itself. Ignoring");
- unactionedPackages.add(packageName);
- continue;
- }
- final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ if (callingPackage.equals(packageName)) {
+ Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+ + (suspended ? "" : "un") + "suspend itself. Ignoring");
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ PackageSetting pkgSetting;
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null
|| filterAppAccessLPr(pkgSetting, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
@@ -12851,15 +12848,20 @@
unactionedPackages.add(packageName);
continue;
}
- if (suspended && !canSuspendPackageForUserLocked(packageName, userId)) {
- unactionedPackages.add(packageName);
- continue;
- }
- pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
- launcherExtras, userId);
- changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
+ if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting != null) {
+ pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
+ launcherExtras, userId);
+ }
+ }
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -13016,16 +13018,13 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- synchronized (mPackages) {
- return canSuspendPackageForUserLocked(packageName, userId);
- }
+ return canSuspendPackageForUserInternal(packageName, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- @GuardedBy("mPackages")
- private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
+ private boolean canSuspendPackageForUserInternal(String packageName, int userId) {
if (isPackageDeviceAdmin(packageName, userId)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": has an active device admin");
@@ -13069,21 +13068,23 @@
return false;
}
- if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": protected package");
- return false;
- }
+ synchronized (mPackages) {
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": protected package");
+ return false;
+ }
- // Cannot suspend static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing static shared library: "
- + pkg.staticSharedLibName);
- return false;
+ // Cannot suspend static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing static shared library: "
+ + pkg.staticSharedLibName);
+ return false;
+ }
}
if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
@@ -15268,9 +15269,12 @@
final DeletePackageAction deletePackageAction;
// we only want to try to delete for non system apps
if (prepareResult.replace && !prepareResult.system) {
+ final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
deletePackageAction = mayDeletePackageLocked(res.removedInfo,
prepareResult.originalPs, prepareResult.disabledPs,
- prepareResult.childPackageSettings);
+ prepareResult.childPackageSettings, deleteFlags, installArgs.user);
if (deletePackageAction == null) {
throw new ReconcileFailure(
PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
@@ -15352,12 +15356,9 @@
}
}
} else {
- final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA
- | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
try {
executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
- null, true, request.mAllUsers, deleteFlags, true, pkg);
+ true, request.mAllUsers, true, pkg);
} catch (SystemDeleteException e) {
if (Build.IS_ENG) {
throw new RuntimeException("Unexpected failure", e);
@@ -17817,12 +17818,23 @@
public final PackageSetting deletingPs;
public final PackageSetting disabledPs;
public final PackageRemovedInfo outInfo;
+ public final int flags;
+ public final UserHandle user;
+ /**
+ * True if this package is an unupdated system app that may be deleted by the system.
+ * When true, disabledPs will be null.
+ */
+ public final boolean mayDeleteUnupdatedSystemApp;
private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
- PackageRemovedInfo outInfo) {
+ PackageRemovedInfo outInfo, int flags, UserHandle user,
+ boolean mayDeleteUnupdatedSystemApp) {
this.deletingPs = deletingPs;
this.disabledPs = disabledPs;
this.outInfo = outInfo;
+ this.flags = flags;
+ this.user = user;
+ this.mayDeleteUnupdatedSystemApp = mayDeleteUnupdatedSystemApp;
}
}
@@ -17834,23 +17846,26 @@
@GuardedBy("mPackages")
private static DeletePackageAction mayDeletePackageLocked(
PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
- @Nullable PackageSetting[] children) {
+ @Nullable PackageSetting[] children, int flags, UserHandle user) {
if (ps == null) {
return null;
}
+ boolean mayDeleteUnupdatedSystemApp = false;
if (isSystemApp(ps)) {
if (ps.parentPackageName != null) {
Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
return null;
}
- // Confirm if the system package has been updated
- // An updated system app can be deleted. This will also have to restore
- // the system pkg from system partition
- // reader
- if (disabledPs == null) {
- Slog.w(TAG,
- "Attempt to delete unknown system package " + ps.pkg.packageName);
+ if (((flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
+ && user.getIdentifier() != UserHandle.USER_ALL) {
+ mayDeleteUnupdatedSystemApp = true;
+ } else if (disabledPs == null) {
+ // Confirmed if the system package has been updated
+ // An updated system app can be deleted. This will also have to restore
+ // the system pkg from system partition
+ // reader
+ Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.packageName);
return null;
}
}
@@ -17867,7 +17882,8 @@
}
}
}
- return new DeletePackageAction(ps, disabledPs, outInfo);
+ return new DeletePackageAction(ps, disabledPs, outInfo, flags, user,
+ mayDeleteUnupdatedSystemApp);
}
/*
@@ -17882,7 +17898,7 @@
final PackageSetting ps = mSettings.mPackages.get(packageName);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
- action = mayDeletePackageLocked(outInfo, ps, disabledPs, children);
+ action = mayDeletePackageLocked(outInfo, ps, disabledPs, children, flags, user);
}
if (null == action) {
return false;
@@ -17891,8 +17907,8 @@
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
try {
- executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
- allUserHandles, flags, writeSettings, replacingPackage);
+ executeDeletePackageLIF(action, packageName, deleteCodeAndResources,
+ allUserHandles, writeSettings, replacingPackage);
} catch (SystemDeleteException e) {
return false;
}
@@ -17909,11 +17925,13 @@
/** Deletes a package. Only throws when install of a disabled package fails. */
private void executeDeletePackageLIF(DeletePackageAction action,
- String packageName, UserHandle user, boolean deleteCodeAndResources,
- int[] allUserHandles, int flags, boolean writeSettings,
+ String packageName, boolean deleteCodeAndResources,
+ int[] allUserHandles, boolean writeSettings,
PackageParser.Package replacingPackage) throws SystemDeleteException {
final PackageSetting ps = action.deletingPs;
final PackageRemovedInfo outInfo = action.outInfo;
+ final UserHandle user = action.user;
+ final int flags = action.flags;
final boolean systemApp = isSystemApp(ps);
synchronized (mPackages) {
@@ -17926,7 +17944,7 @@
final int removedUserId = (user != null) ? user.getIdentifier()
: UserHandle.USER_ALL;
- clearPackageStateForUserLIF(ps, removedUserId, outInfo);
+ clearPackageStateForUserLIF(ps, removedUserId, outInfo, flags);
markPackageUninstalledForUserLPw(ps, user);
scheduleWritePackageRestrictionsLocked(user);
return;
@@ -17939,8 +17957,7 @@
}
- if (((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
- && user.getIdentifier() != UserHandle.USER_ALL)) {
+ if (!systemApp || action.mayDeleteUnupdatedSystemApp) {
// The caller is asking that the package only be deleted for a single
// user. To do this, we just mark its uninstalled state and delete
// its data. If this is a system app, we only allow this to happen if
@@ -17956,7 +17973,7 @@
// we need to do is clear this user's data and save that
// it is uninstalled.
if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
- clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
+ clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo, flags);
scheduleWritePackageRestrictionsLocked(user);
return;
} else {
@@ -17972,7 +17989,7 @@
// we need to do is clear this user's data and save that
// it is uninstalled.
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
- clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
+ clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo, flags);
scheduleWritePackageRestrictionsLocked(user);
return;
}
@@ -18089,7 +18106,7 @@
}
private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
- PackageRemovedInfo outInfo) {
+ PackageRemovedInfo outInfo, int flags) {
final PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(ps.name);
@@ -18114,6 +18131,14 @@
}
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);
}
+ // Also delete contributed media, when requested
+ if ((flags & PackageManager.DELETE_CONTRIBUTED_MEDIA) != 0) {
+ try {
+ MediaStore.deleteContributedMedia(mContext, ps.name, UserHandle.of(nextUserId));
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to delete contributed media for " + ps.name, e);
+ }
+ }
}
if (outInfo != null) {
@@ -18300,6 +18325,10 @@
continue;
}
+ if (bp.isRemoved()) {
+ continue;
+ }
+
// If shared user we just reset the state to which only this app contributed.
if (ps.sharedUser != null) {
boolean used = false;
@@ -19556,6 +19585,27 @@
return mContext.getString(R.string.config_defaultTextClassifierPackage);
}
+ private @Nullable String getDocumenterPackageName() {
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+
+ final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_DISABLED_COMPONENTS,
+ UserHandle.myUserId());
+ if (matches.size() == 1) {
+ return matches.get(0).getComponentInfo().packageName;
+ } else {
+ Slog.e(TAG, "There should probably be exactly one documenter; found "
+ + matches.size() + ": matches=" + matches);
+ return null;
+ }
+ }
+
+ @Override
+ public String getWellbeingPackageName() {
+ return mContext.getString(R.string.config_defaultWellbeingPackage);
+ }
+
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
@@ -22712,6 +22762,10 @@
return mSystemTextClassifierPackage;
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return mRequiredPermissionControllerPackage;
+ case PackageManagerInternal.PACKAGE_WELLBEING:
+ return mWellbeingPackage;
+ case PackageManagerInternal.PACKAGE_DOCUMENTER:
+ return mDocumenterPackage;
}
return null;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 77f8c3a..37a35a2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -536,6 +536,7 @@
boolean listInstaller = false;
boolean showUid = false;
boolean showVersionCode = false;
+ boolean listApexOnly = false;
int uid = -1;
int userId = UserHandle.USER_SYSTEM;
try {
@@ -576,6 +577,10 @@
case "--show-versioncode":
showVersionCode = true;
break;
+ case "--apex-only":
+ getFlags |= PackageManager.MATCH_APEX;
+ listApexOnly = true;
+ break;
case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
@@ -606,30 +611,34 @@
if (filter != null && !info.packageName.contains(filter)) {
continue;
}
- if (uid != -1 && info.applicationInfo.uid != uid) {
+ final boolean isApex = info.isApex;
+ if (uid != -1 && !isApex && info.applicationInfo.uid != uid) {
continue;
}
- final boolean isSystem =
+
+ final boolean isSystem = !isApex &&
(info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
- if ((!listDisabled || !info.applicationInfo.enabled) &&
- (!listEnabled || info.applicationInfo.enabled) &&
+ final boolean isEnabled = !isApex && info.applicationInfo.enabled;
+ if ((!listDisabled || !isEnabled) &&
+ (!listEnabled || isEnabled) &&
(!listSystem || isSystem) &&
- (!listThirdParty || !isSystem)) {
+ (!listThirdParty || !isSystem) &&
+ (!listApexOnly || isApex)) {
pw.print("package:");
- if (showSourceDir) {
+ if (showSourceDir && !isApex) {
pw.print(info.applicationInfo.sourceDir);
pw.print("=");
}
pw.print(info.packageName);
- if (showVersionCode) {
+ if (showVersionCode && !isApex) {
pw.print(" versionCode:");
pw.print(info.applicationInfo.versionCode);
}
- if (listInstaller) {
+ if (listInstaller && !isApex) {
pw.print(" installer=");
pw.print(mInterface.getInstallerPackageName(info.packageName));
}
- if (showUid) {
+ if (showUid && !isApex) {
pw.print(" uid:");
pw.print(info.applicationInfo.uid);
}
@@ -2284,6 +2293,9 @@
case "--multi-package":
sessionParams.setMultiPackage();
break;
+ case "--staged":
+ sessionParams.setStaged();
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -2767,11 +2779,11 @@
pw.println(" Prints all system libraries.");
pw.println("");
pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
- pw.println(" [--uid UID] [--user USER_ID] [FILTER]");
+ pw.println(" [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
- pw.println(" -a: all known packages");
+ pw.println(" -a: all known packages (but excluding APEXes)");
pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
pw.println(" -s: filter to only show system packages");
@@ -2780,6 +2792,7 @@
pw.println(" -l: ignored (used for compatibility with older releases)");
pw.println(" -U: also show the package UID");
pw.println(" -u: also include uninstalled packages");
+ pw.println(" --apex-only: only show APEX packages");
pw.println(" --uid UID: filter to only show packages with the given UID");
pw.println(" --user USER_ID: only list packages belonging to the given user");
pw.println("");
@@ -2853,7 +2866,7 @@
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
- pw.println(" [--multi-package]");
+ pw.println(" [--multi-package] [--staged]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
pw.println(" to push data into the session, and \"install-commit\" to finish.");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c18ca25..d1d5818 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -31,6 +31,7 @@
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyEventLogger;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -73,6 +74,7 @@
import android.os.storage.StorageManager;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.util.AtomicFile;
import android.util.IntArray;
import android.util.Log;
@@ -100,6 +102,8 @@
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -121,8 +125,6 @@
import java.util.List;
import java.util.Objects;
-import libcore.io.IoUtils;
-
/**
* Service for {@link UserManager}.
*
@@ -173,6 +175,8 @@
private static final String TAG_ENTRY = "entry";
private static final String TAG_VALUE = "value";
private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
+ private static final String TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL =
+ "lastRequestQuietModeEnabledCall";
private static final String ATTR_KEY = "key";
private static final String ATTR_VALUE_TYPE = "type";
private static final String ATTR_MULTIPLE = "m";
@@ -268,6 +272,16 @@
/** Elapsed realtime since boot when the user was unlocked. */
long unlockRealtime;
+ private long mLastRequestQuietModeEnabledMillis;
+
+ void setLastRequestQuietModeEnabledMillis(long millis) {
+ mLastRequestQuietModeEnabledMillis = millis;
+ }
+
+ long getLastRequestQuietModeEnabledMillis() {
+ return mLastRequestQuietModeEnabledMillis;
+ }
+
void clearSeedAccountData() {
seedAccountName = null;
seedAccountType = null;
@@ -389,8 +403,8 @@
final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
// Call setQuietModeEnabled on bg thread to avoid ANR
- BackgroundThread.getHandler()
- .post(() -> setQuietModeEnabled(userHandle, false, target));
+ BackgroundThread.getHandler().post(() ->
+ setQuietModeEnabled(userHandle, false, target, /* callingPackage */ null));
}
};
@@ -834,21 +848,24 @@
ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), target != null);
final long identity = Binder.clearCallingIdentity();
try {
+ boolean result = false;
if (enableQuietMode) {
- setQuietModeEnabled(userHandle, true /* enableQuietMode */, target);
- return true;
+ setQuietModeEnabled(
+ userHandle, true /* enableQuietMode */, target, callingPackage);
+ result = true;
} else {
boolean needToShowConfirmCredential =
mLockPatternUtils.isSecure(userHandle)
&& !StorageManager.isUserKeyUnlocked(userHandle);
if (needToShowConfirmCredential) {
showConfirmCredentialToDisableQuietMode(userHandle, target);
- return false;
} else {
- setQuietModeEnabled(userHandle, false /* enableQuietMode */, target);
- return true;
+ setQuietModeEnabled(
+ userHandle, false /* enableQuietMode */, target, callingPackage);
+ result = true;
}
}
+ return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -894,8 +911,8 @@
+ "default launcher nor has MANAGE_USERS/MODIFY_QUIET_MODE permission");
}
- private void setQuietModeEnabled(
- int userHandle, boolean enableQuietMode, IntentSender target) {
+ private void setQuietModeEnabled(int userHandle, boolean enableQuietMode,
+ IntentSender target, @Nullable String callingPackage) {
final UserInfo profile, parent;
final UserData profileUserData;
synchronized (mUsersLock) {
@@ -927,6 +944,7 @@
ActivityManager.getService().startUserInBackgroundWithListener(
userHandle, callback);
}
+ logQuietModeEnabled(userHandle, enableQuietMode, callingPackage);
} catch (RemoteException e) {
// Should not happen, same process.
e.rethrowAsRuntimeException();
@@ -935,6 +953,28 @@
enableQuietMode);
}
+ private void logQuietModeEnabled(int userHandle, boolean enableQuietMode,
+ @Nullable String callingPackage) {
+ UserData userData;
+ synchronized (mUsersLock) {
+ userData = getUserDataLU(userHandle);
+ }
+ if (userData == null) {
+ return;
+ }
+ final long now = System.currentTimeMillis();
+ final long period = (userData.getLastRequestQuietModeEnabledMillis() != 0L
+ ? now - userData.getLastRequestQuietModeEnabledMillis()
+ : now - userData.info.creationTime);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.REQUEST_QUIET_MODE_ENABLED)
+ .setStrings(callingPackage)
+ .setBoolean(enableQuietMode)
+ .setTimePeriod(period)
+ .write();
+ userData.setLastRequestQuietModeEnabledMillis(now);
+ }
+
@Override
public boolean isQuietModeEnabled(int userHandle) {
synchronized (mPackagesLock) {
@@ -2314,6 +2354,12 @@
serializer.endTag(null, TAG_SEED_ACCOUNT_OPTIONS);
}
+ if (userData.getLastRequestQuietModeEnabledMillis() != 0L) {
+ serializer.startTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL);
+ serializer.text(String.valueOf(userData.getLastRequestQuietModeEnabledMillis()));
+ serializer.endTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL);
+ }
+
serializer.endTag(null, TAG_USER);
serializer.endDocument();
@@ -2408,6 +2454,7 @@
String iconPath = null;
long creationTime = 0L;
long lastLoggedInTime = 0L;
+ long lastRequestQuietModeEnabledTimestamp = 0L;
String lastLoggedInFingerprint = null;
int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
int profileBadge = 0;
@@ -2494,6 +2541,11 @@
} else if (TAG_SEED_ACCOUNT_OPTIONS.equals(tag)) {
seedAccountOptions = PersistableBundle.restoreFromXml(parser);
persistSeedData = true;
+ } else if (TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ lastRequestQuietModeEnabledTimestamp = Long.parseLong(parser.getText());
+ }
}
}
}
@@ -2518,6 +2570,7 @@
userData.seedAccountType = seedAccountType;
userData.persistSeedData = persistSeedData;
userData.seedAccountOptions = seedAccountOptions;
+ userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp);
synchronized (mRestrictionsLock) {
if (baseRestrictions != null) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 3a74ab5..36b7269 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -16,6 +16,11 @@
package com.android.server.pm.dex;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -26,9 +31,9 @@
import android.os.Build;
import android.os.FileUtils;
import android.os.RemoteException;
-import android.os.storage.StorageManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.provider.Settings.Global;
import android.util.Log;
import android.util.Slog;
@@ -48,18 +53,14 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
-import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
-
/**
* This class keeps track of how dex files are used.
* Every time it gets a notification about a dex file being loaded it tracks
@@ -89,6 +90,12 @@
// encode and save the dex usage data.
private final PackageDexUsage mPackageDexUsage;
+ // PackageDynamicCodeLoading handles recording of dynamic code loading -
+ // which is similar to PackageDexUsage but records a different aspect of the data.
+ // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't
+ // record class loaders or ISAs.)
+ private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
+
private final IPackageManager mPackageManager;
private final PackageDexOptimizer mPackageDexOptimizer;
private final Object mInstallLock;
@@ -126,14 +133,15 @@
public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo,
Installer installer, Object installLock, Listener listener) {
- mContext = context;
- mPackageCodeLocationsCache = new HashMap<>();
- mPackageDexUsage = new PackageDexUsage();
- mPackageManager = pms;
- mPackageDexOptimizer = pdo;
- mInstaller = installer;
- mInstallLock = installLock;
- mListener = listener;
+ mContext = context;
+ mPackageCodeLocationsCache = new HashMap<>();
+ mPackageDexUsage = new PackageDexUsage();
+ mPackageDynamicCodeLoading = new PackageDynamicCodeLoading();
+ mPackageManager = pms;
+ mPackageDexOptimizer = pdo;
+ mInstaller = installer;
+ mInstallLock = installLock;
+ mListener = listener;
}
public void systemReady() {
@@ -207,7 +215,6 @@
Slog.i(TAG, loadingAppInfo.packageName +
" uses unsupported class loader in " + classLoaderNames);
}
- return;
}
int dexPathIndex = 0;
@@ -236,15 +243,24 @@
continue;
}
- // Record dex file usage. If the current usage is a new pattern (e.g. new secondary,
- // or UsedByOtherApps), record will return true and we trigger an async write
- // to disk to make sure we don't loose the data in case of a reboot.
+ if (mPackageDynamicCodeLoading.record(searchResult.mOwningPackageName, dexPath,
+ PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
+ loadingAppInfo.packageName)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
- String classLoaderContext = classLoaderContexts[dexPathIndex];
- if (mPackageDexUsage.record(searchResult.mOwningPackageName,
- dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
- loadingAppInfo.packageName, classLoaderContext)) {
- mPackageDexUsage.maybeWriteAsync();
+ if (classLoaderContexts != null) {
+
+ // Record dex file usage. If the current usage is a new pattern (e.g. new
+ // secondary, or UsedByOtherApps), record will return true and we trigger an
+ // async write to disk to make sure we don't loose the data in case of a reboot.
+
+ String classLoaderContext = classLoaderContexts[dexPathIndex];
+ if (mPackageDexUsage.record(searchResult.mOwningPackageName,
+ dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+ loadingAppInfo.packageName, classLoaderContext)) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
}
} else {
// If we can't find the owner of the dex we simply do not track it. The impact is
@@ -268,8 +284,8 @@
loadInternal(existingPackages);
} catch (Exception e) {
mPackageDexUsage.clear();
- Slog.w(TAG, "Exception while loading package dex usage. " +
- "Starting with a fresh state.", e);
+ mPackageDynamicCodeLoading.clear();
+ Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
}
}
@@ -311,15 +327,24 @@
* all usage information for the package will be removed.
*/
public void notifyPackageDataDestroyed(String packageName, int userId) {
- boolean updated = userId == UserHandle.USER_ALL
- ? mPackageDexUsage.removePackage(packageName)
- : mPackageDexUsage.removeUserPackage(packageName, userId);
// In case there was an update, write the package use info to disk async.
- // Note that we do the writing here and not in PackageDexUsage in order to be
+ // Note that we do the writing here and not in the lower level classes in order to be
// consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
// multiple updates in PackageDexUsage before writing it).
- if (updated) {
- mPackageDexUsage.maybeWriteAsync();
+ if (userId == UserHandle.USER_ALL) {
+ if (mPackageDexUsage.removePackage(packageName)) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+ if (mPackageDynamicCodeLoading.removePackage(packageName)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
+ } else {
+ if (mPackageDexUsage.removeUserPackage(packageName, userId)) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+ if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
}
}
@@ -388,8 +413,23 @@
}
}
- mPackageDexUsage.read();
- mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+ try {
+ mPackageDexUsage.read();
+ mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+ } catch (Exception e) {
+ mPackageDexUsage.clear();
+ Slog.w(TAG, "Exception while loading package dex usage. "
+ + "Starting with a fresh state.", e);
+ }
+
+ try {
+ mPackageDynamicCodeLoading.read();
+ mPackageDynamicCodeLoading.syncData(packageToUsersMap);
+ } catch (Exception e) {
+ mPackageDynamicCodeLoading.clear();
+ Slog.w(TAG, "Exception while loading package dynamic code usage. "
+ + "Starting with a fresh state.", e);
+ }
}
/**
@@ -415,10 +455,16 @@
* TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
* to access the package use.
*/
+ @VisibleForTesting
/*package*/ boolean hasInfoOnPackage(String packageName) {
return mPackageDexUsage.getPackageUseInfo(packageName) != null;
}
+ @VisibleForTesting
+ /*package*/ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+ return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
+ }
+
/**
* Perform dexopt on with the given {@code options} on the secondary dex files.
* @return true if all secondary dex files were processed successfully (compiled or skipped
@@ -652,7 +698,7 @@
// to load dex files through it.
try {
String dexPathReal = PackageManagerServiceUtils.realpath(new File(dexPath));
- if (dexPathReal != dexPath) {
+ if (!dexPath.equals(dexPathReal)) {
Slog.d(TAG, "Dex loaded with symlink. dexPath=" +
dexPath + " dexPathReal=" + dexPathReal);
}
@@ -675,6 +721,7 @@
*/
public void writePackageDexUsageNow() {
mPackageDexUsage.writeNow();
+ mPackageDynamicCodeLoading.writeNow();
}
private void registerSettingObserver() {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
new file mode 100644
index 0000000..f74aa1d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastPrintWriter;
+import com.android.server.pm.AbstractStatsBase;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Stats file which stores information about secondary code files that are dynamically loaded.
+ */
+class PackageDynamicCodeLoading extends AbstractStatsBase<Void> {
+ // Type code to indicate a secondary file containing DEX code. (The char value is how it
+ // is represented in the text file format.)
+ static final int FILE_TYPE_DEX = 'D';
+
+ private static final String TAG = "PackageDynamicCodeLoading";
+
+ private static final String FILE_VERSION_HEADER = "DCL1";
+ private static final String PACKAGE_PREFIX = "P:";
+
+ private static final char FIELD_SEPARATOR = ':';
+ private static final String PACKAGE_SEPARATOR = ",";
+
+ /**
+ * Regular expression to match the expected format of an input line describing one file.
+ * <p>Example: {@code D:10:package.name1,package.name2:/escaped/path}
+ * <p>The capturing groups are the file type, user ID, loading packages and escaped file path
+ * (in that order).
+ * <p>See {@link #write(OutputStream, Map)} below for more details of the format.
+ */
+ private static final Pattern PACKAGE_LINE_PATTERN =
+ Pattern.compile("([A-Z]):([0-9]+):([^:]*):(.*)");
+
+ private final Object mLock = new Object();
+
+ // Map from package name to data about loading of dynamic code files owned by that package.
+ // (Apps may load code files owned by other packages, subject to various access
+ // constraints.)
+ // Any PackageDynamicCode in this map will be non-empty.
+ @GuardedBy("mLock")
+ private Map<String, PackageDynamicCode> mPackageMap = new HashMap<>();
+
+ PackageDynamicCodeLoading() {
+ super("package-dcl.list", "PackageDynamicCodeLoading_DiskWriter", false);
+ }
+
+ /**
+ * Record dynamic code loading from a file.
+ *
+ * Note this is called when an app loads dex files and as such it should return
+ * as fast as possible.
+ *
+ * @param owningPackageName the package owning the file path
+ * @param filePath the path of the dex files being loaded
+ * @param fileType the type of code loading
+ * @param ownerUserId the user id which runs the code loading the file
+ * @param loadingPackageName the package performing the load
+ * @return whether new information has been recorded
+ * @throws IllegalArgumentException if clearly invalid information is detected
+ */
+ boolean record(String owningPackageName, String filePath, int fileType, int ownerUserId,
+ String loadingPackageName) {
+ if (fileType != FILE_TYPE_DEX) {
+ throw new IllegalArgumentException("Bad file type: " + fileType);
+ }
+ synchronized (mLock) {
+ PackageDynamicCode packageInfo = mPackageMap.get(owningPackageName);
+ if (packageInfo == null) {
+ packageInfo = new PackageDynamicCode();
+ mPackageMap.put(owningPackageName, packageInfo);
+ }
+ return packageInfo.add(filePath, (char) fileType, ownerUserId, loadingPackageName);
+ }
+ }
+
+ /**
+ * Return all packages that contain records of secondary dex files. (Note that data updates
+ * asynchronously, so {@link #getPackageDynamicCodeInfo} may still return null if passed
+ * one of these package names.)
+ */
+ Set<String> getAllPackagesWithDynamicCodeLoading() {
+ synchronized (mLock) {
+ return new HashSet<>(mPackageMap.keySet());
+ }
+ }
+
+ /**
+ * Return information about the dynamic code file usage of the specified package,
+ * or null if there is currently no usage information. The object returned is a copy of the
+ * live information that is not updated.
+ */
+ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+ synchronized (mLock) {
+ PackageDynamicCode info = mPackageMap.get(packageName);
+ return info == null ? null : new PackageDynamicCode(info);
+ }
+ }
+
+ /**
+ * Remove all information about all packages.
+ */
+ void clear() {
+ synchronized (mLock) {
+ mPackageMap.clear();
+ }
+ }
+
+ /**
+ * Remove the data associated with package {@code packageName}. Affects all users.
+ * @return true if the package usage was found and removed successfully
+ */
+ boolean removePackage(String packageName) {
+ synchronized (mLock) {
+ return mPackageMap.remove(packageName) != null;
+ }
+ }
+
+ /**
+ * Remove all the records about package {@code packageName} belonging to user {@code userId}.
+ * @return whether any data was actually removed
+ */
+ boolean removeUserPackage(String packageName, int userId) {
+ synchronized (mLock) {
+ PackageDynamicCode packageDynamicCode = mPackageMap.get(packageName);
+ if (packageDynamicCode == null) {
+ return false;
+ }
+ if (packageDynamicCode.removeUser(userId)) {
+ if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+ mPackageMap.remove(packageName);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Remove the specified dynamic code file record belonging to the package {@code packageName}
+ * and user {@code userId}.
+ * @return whether data was actually removed
+ */
+ boolean removeFile(String packageName, String filePath, int userId) {
+ synchronized (mLock) {
+ PackageDynamicCode packageDynamicCode = mPackageMap.get(packageName);
+ if (packageDynamicCode == null) {
+ return false;
+ }
+ if (packageDynamicCode.removeFile(filePath, userId)) {
+ if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+ mPackageMap.remove(packageName);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Syncs data with the set of installed packages. Data about packages that are no longer
+ * installed is removed.
+ * @param packageToUsersMap a map from all existing package names to the users who have the
+ * package installed
+ */
+ void syncData(Map<String, Set<Integer>> packageToUsersMap) {
+ synchronized (mLock) {
+ Iterator<Entry<String, PackageDynamicCode>> it = mPackageMap.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, PackageDynamicCode> entry = it.next();
+ Set<Integer> packageUsers = packageToUsersMap.get(entry.getKey());
+ if (packageUsers == null) {
+ it.remove();
+ } else {
+ PackageDynamicCode packageDynamicCode = entry.getValue();
+ packageDynamicCode.syncData(packageToUsersMap, packageUsers);
+ if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Request that data be written to persistent file at the next time allowed by write-limiting.
+ */
+ void maybeWriteAsync() {
+ super.maybeWriteAsync(null);
+ }
+
+ /**
+ * Writes data to persistent file immediately.
+ */
+ void writeNow() {
+ super.writeNow(null);
+ }
+
+ @Override
+ protected final void writeInternal(Void data) {
+ AtomicFile file = getFile();
+ FileOutputStream output = null;
+ try {
+ output = file.startWrite();
+ write(output);
+ file.finishWrite(output);
+ } catch (IOException e) {
+ file.failWrite(output);
+ Slog.e(TAG, "Failed to write dynamic usage for secondary code files.", e);
+ }
+ }
+
+ @VisibleForTesting
+ void write(OutputStream output) throws IOException {
+ // Make a deep copy to avoid holding the lock while writing to disk.
+ Map<String, PackageDynamicCode> copiedMap;
+ synchronized (mLock) {
+ copiedMap = new HashMap<>(mPackageMap.size());
+ for (Entry<String, PackageDynamicCode> entry : mPackageMap.entrySet()) {
+ PackageDynamicCode copiedValue = new PackageDynamicCode(entry.getValue());
+ copiedMap.put(entry.getKey(), copiedValue);
+ }
+ }
+
+ write(output, copiedMap);
+ }
+
+ /**
+ * Write the dynamic code loading data as a text file to {@code output}. The file format begins
+ * with a line indicating the file type and version - {@link #FILE_VERSION_HEADER}.
+ * <p>There is then one section for each owning package, introduced by a line beginning "P:".
+ * This is followed by a line for each file owned by the package this is dynamically loaded,
+ * containing the file type, user ID, loading package names and full path (with newlines and
+ * backslashes escaped - see {@link #escape}).
+ * <p>For example:
+ * <pre>{@code
+ * DCL1
+ * P:first.owning.package
+ * D:0:loading.package_1,loading.package_2:/path/to/file
+ * D:10:loading.package_1:/another/file
+ * P:second.owning.package
+ * D:0:loading.package:/third/file
+ * }</pre>
+ */
+ private static void write(OutputStream output, Map<String, PackageDynamicCode> packageMap)
+ throws IOException {
+ PrintWriter writer = new FastPrintWriter(output);
+
+ writer.println(FILE_VERSION_HEADER);
+ for (Entry<String, PackageDynamicCode> packageEntry : packageMap.entrySet()) {
+ writer.print(PACKAGE_PREFIX);
+ writer.println(packageEntry.getKey());
+
+ Map<String, DynamicCodeFile> mFileUsageMap = packageEntry.getValue().mFileUsageMap;
+ for (Entry<String, DynamicCodeFile> fileEntry : mFileUsageMap.entrySet()) {
+ String path = fileEntry.getKey();
+ DynamicCodeFile dynamicCodeFile = fileEntry.getValue();
+
+ writer.print(dynamicCodeFile.mFileType);
+ writer.print(FIELD_SEPARATOR);
+ writer.print(dynamicCodeFile.mUserId);
+ writer.print(FIELD_SEPARATOR);
+
+ String prefix = "";
+ for (String packageName : dynamicCodeFile.mLoadingPackages) {
+ writer.print(prefix);
+ writer.print(packageName);
+ prefix = PACKAGE_SEPARATOR;
+ }
+
+ writer.print(FIELD_SEPARATOR);
+ writer.println(escape(path));
+ }
+ }
+
+ writer.flush();
+ if (writer.checkError()) {
+ throw new IOException("Writer failed");
+ }
+ }
+
+ /**
+ * Read data from the persistent file. Replaces existing data completely if successful.
+ */
+ void read() {
+ super.read(null);
+ }
+
+ @Override
+ protected final void readInternal(Void data) {
+ AtomicFile file = getFile();
+
+ FileInputStream stream = null;
+ try {
+ stream = file.openRead();
+ read(stream);
+ } catch (FileNotFoundException expected) {
+ // The file may not be there. E.g. When we first take the OTA with this feature.
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to parse dynamic usage for secondary code files.", e);
+ } finally {
+ IoUtils.closeQuietly(stream);
+ }
+ }
+
+ @VisibleForTesting
+ void read(InputStream stream) throws IOException {
+ Map<String, PackageDynamicCode> newPackageMap = new HashMap<>();
+ read(stream, newPackageMap);
+ synchronized (mLock) {
+ mPackageMap = newPackageMap;
+ }
+ }
+
+ private static void read(InputStream stream, Map<String, PackageDynamicCode> packageMap)
+ throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+ String versionLine = reader.readLine();
+ if (!FILE_VERSION_HEADER.equals(versionLine)) {
+ throw new IOException("Incorrect version line: " + versionLine);
+ }
+
+ String line = reader.readLine();
+ if (line != null && !line.startsWith(PACKAGE_PREFIX)) {
+ throw new IOException("Malformed line: " + line);
+ }
+
+ while (line != null) {
+ String packageName = line.substring(PACKAGE_PREFIX.length());
+
+ PackageDynamicCode packageInfo = new PackageDynamicCode();
+ while (true) {
+ line = reader.readLine();
+ if (line == null || line.startsWith(PACKAGE_PREFIX)) {
+ break;
+ }
+ readFileInfo(line, packageInfo);
+ }
+
+ if (!packageInfo.mFileUsageMap.isEmpty()) {
+ packageMap.put(packageName, packageInfo);
+ }
+ }
+ }
+
+ private static void readFileInfo(String line, PackageDynamicCode output) throws IOException {
+ try {
+ Matcher matcher = PACKAGE_LINE_PATTERN.matcher(line);
+ if (!matcher.matches()) {
+ throw new IOException("Malformed line: " + line);
+ }
+
+ char type = matcher.group(1).charAt(0);
+ int user = Integer.parseInt(matcher.group(2));
+ String[] packages = matcher.group(3).split(PACKAGE_SEPARATOR);
+ String path = unescape(matcher.group(4));
+
+ if (packages.length == 0) {
+ throw new IOException("Malformed line: " + line);
+ }
+ if (type != FILE_TYPE_DEX) {
+ throw new IOException("Unknown file type: " + line);
+ }
+
+ output.mFileUsageMap.put(path, new DynamicCodeFile(type, user, packages));
+ } catch (RuntimeException e) {
+ // Just in case we get NumberFormatException, or various
+ // impossible out of bounds errors happen.
+ throw new IOException("Unable to parse line: " + line, e);
+ }
+ }
+
+ /**
+ * Escape any newline and backslash characters in path. A newline in a path is legal if unusual,
+ * and it would break our line-based file parsing.
+ */
+ @VisibleForTesting
+ static String escape(String path) {
+ if (path.indexOf('\\') == -1 && path.indexOf('\n') == -1 && path.indexOf('\r') == -1) {
+ return path;
+ }
+
+ StringBuilder result = new StringBuilder(path.length() + 10);
+ for (int i = 0; i < path.length(); i++) {
+ // Surrogates will never match the characters we care about, so it's ok to use chars
+ // not code points here.
+ char c = path.charAt(i);
+ switch (c) {
+ case '\\':
+ result.append("\\\\");
+ break;
+ case '\n':
+ result.append("\\n");
+ break;
+ case '\r':
+ result.append("\\r");
+ break;
+ default:
+ result.append(c);
+ break;
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Reverse the effect of {@link #escape}.
+ * @throws IOException if the input string is malformed
+ */
+ @VisibleForTesting
+ static String unescape(String escaped) throws IOException {
+ // As we move through the input string, start is the position of the first character
+ // after the previous escape sequence and finish is the position of the following backslash.
+ int start = 0;
+ int finish = escaped.indexOf('\\');
+ if (finish == -1) {
+ return escaped;
+ }
+
+ StringBuilder result = new StringBuilder(escaped.length());
+ while (true) {
+ if (finish >= escaped.length() - 1) {
+ // Backslash mustn't be the last character
+ throw new IOException("Unexpected \\ in: " + escaped);
+ }
+ result.append(escaped, start, finish);
+ switch (escaped.charAt(finish + 1)) {
+ case '\\':
+ result.append('\\');
+ break;
+ case 'r':
+ result.append('\r');
+ break;
+ case 'n':
+ result.append('\n');
+ break;
+ default:
+ throw new IOException("Bad escape in: " + escaped);
+ }
+
+ start = finish + 2;
+ finish = escaped.indexOf('\\', start);
+ if (finish == -1) {
+ result.append(escaped, start, escaped.length());
+ break;
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Represents the dynamic code usage of a single package.
+ */
+ static class PackageDynamicCode {
+ /**
+ * Map from secondary code file path to information about which packages dynamically load
+ * that file.
+ */
+ final Map<String, DynamicCodeFile> mFileUsageMap;
+
+ private PackageDynamicCode() {
+ mFileUsageMap = new HashMap<>();
+ }
+
+ private PackageDynamicCode(PackageDynamicCode original) {
+ mFileUsageMap = new HashMap<>(original.mFileUsageMap.size());
+ for (Entry<String, DynamicCodeFile> entry : original.mFileUsageMap.entrySet()) {
+ DynamicCodeFile newValue = new DynamicCodeFile(entry.getValue());
+ mFileUsageMap.put(entry.getKey(), newValue);
+ }
+ }
+
+ private boolean add(String path, char fileType, int userId, String loadingPackage) {
+ DynamicCodeFile fileInfo = mFileUsageMap.get(path);
+ if (fileInfo == null) {
+ fileInfo = new DynamicCodeFile(fileType, userId, loadingPackage);
+ mFileUsageMap.put(path, fileInfo);
+ return true;
+ } else {
+ if (fileInfo.mUserId != userId) {
+ // This should be impossible: private app files are always user-specific and
+ // can't be accessed from different users.
+ throw new IllegalArgumentException("Cannot change userId for '" + path
+ + "' from " + fileInfo.mUserId + " to " + userId);
+ }
+ // Changing file type (i.e. loading the same file in different ways is possible if
+ // unlikely. We allow it but ignore it.
+ return fileInfo.mLoadingPackages.add(loadingPackage);
+ }
+ }
+
+ private boolean removeUser(int userId) {
+ boolean updated = false;
+ Iterator<DynamicCodeFile> it = mFileUsageMap.values().iterator();
+ while (it.hasNext()) {
+ DynamicCodeFile fileInfo = it.next();
+ if (fileInfo.mUserId == userId) {
+ it.remove();
+ updated = true;
+ }
+ }
+ return updated;
+ }
+
+ private boolean removeFile(String filePath, int userId) {
+ DynamicCodeFile fileInfo = mFileUsageMap.get(filePath);
+ if (fileInfo == null || fileInfo.mUserId != userId) {
+ return false;
+ } else {
+ mFileUsageMap.remove(filePath);
+ return true;
+ }
+ }
+
+ private void syncData(Map<String, Set<Integer>> packageToUsersMap,
+ Set<Integer> owningPackageUsers) {
+ Iterator<DynamicCodeFile> fileIt = mFileUsageMap.values().iterator();
+ while (fileIt.hasNext()) {
+ DynamicCodeFile fileInfo = fileIt.next();
+ int fileUserId = fileInfo.mUserId;
+ if (!owningPackageUsers.contains(fileUserId)) {
+ fileIt.remove();
+ } else {
+ // Also remove information about any loading packages that are no longer
+ // installed for this user.
+ Iterator<String> loaderIt = fileInfo.mLoadingPackages.iterator();
+ while (loaderIt.hasNext()) {
+ String loader = loaderIt.next();
+ Set<Integer> loadingPackageUsers = packageToUsersMap.get(loader);
+ if (loadingPackageUsers == null
+ || !loadingPackageUsers.contains(fileUserId)) {
+ loaderIt.remove();
+ }
+ }
+ if (fileInfo.mLoadingPackages.isEmpty()) {
+ fileIt.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Represents a single dynamic code file loaded by one or more packages. Note that it is
+ * possible for one app to dynamically load code from a different app's home dir, if the
+ * owning app:
+ * <ul>
+ * <li>Targets API 27 or lower and has shared its home dir.
+ * <li>Is a system app.
+ * <li>Has a shared UID with the loading app.
+ * </ul>
+ */
+ static class DynamicCodeFile {
+ final char mFileType;
+ final int mUserId;
+ final Set<String> mLoadingPackages;
+
+ private DynamicCodeFile(char type, int user, String... packages) {
+ mFileType = type;
+ mUserId = user;
+ mLoadingPackages = new HashSet<>(Arrays.asList(packages));
+ }
+
+ private DynamicCodeFile(DynamicCodeFile original) {
+ mFileType = original.mFileType;
+ mUserId = original.mUserId;
+ mLoadingPackages = new HashSet<>(original.mLoadingPackages);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 2d583ca3..3a49412 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -189,6 +189,12 @@
return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
}
+
+ public boolean isRemoved() {
+ return perm != null && perm.info != null
+ && (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0;
+ }
+
public boolean isSignature() {
return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
PermissionInfo.PROTECTION_SIGNATURE;
@@ -235,6 +241,12 @@
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
!= 0;
}
+ public boolean isWellbeing() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
+ }
+ public boolean isDocumenter() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
+ }
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 68fe1d8..51619cf 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -46,7 +46,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.permission.PermissionManager;
@@ -1059,6 +1058,17 @@
return;
}
+ // Intersect the requestedPermissions for a factory image with that of its current update
+ // in case the latter one removed a <uses-permission>
+ String[] requestedByNonSystemPackage = getPackageInfo(pkg.packageName).requestedPermissions;
+ int size = requestedPermissions.length;
+ for (int i = 0; i < size; i++) {
+ if (!ArrayUtils.contains(requestedByNonSystemPackage, requestedPermissions[i])) {
+ requestedPermissions[i] = null;
+ }
+ }
+ requestedPermissions = ArrayUtils.filterNotNull(requestedPermissions, String[]::new);
+
PackageManager pm = mContext.getPackageManager();
final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
ApplicationInfo applicationInfo = pkg.applicationInfo;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 4406fdd..31f5ce4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -799,6 +799,10 @@
continue;
}
+ if (bp.isRemoved()) {
+ continue;
+ }
+
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
if (DEBUG_PERMISSIONS) {
@@ -1637,6 +1641,19 @@
// Special permissions for the system default text classifier.
allowed = true;
}
+ if (!allowed && bp.isWellbeing()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
+ // Special permission granted only to the OEM specified wellbeing app
+ allowed = true;
+ }
+ if (!allowed && bp.isDocumenter()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
+ // If this permission is to be granted to the documenter and
+ // this app is the documenter, then it gets the permission.
+ allowed = true;
+ }
}
return allowed;
}
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index c5139b5..cedb548 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -111,7 +111,7 @@
false, /* enableAdjustBrightness */
false, /* enableDataSaver */
true, /* enableFirewall */
- false, /* enableQuickDoze */
+ true, /* enableQuickDoze */
new ArrayMap<>(), /* filesForInteractive */
new ArrayMap<>(), /* filesForNoninteractive */
true, /* forceAllAppsStandby */
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 29d6237..565bb706 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3221,6 +3221,20 @@
mNativeWrapper.nativeSendPowerHint(hintId, data);
}
+ @VisibleForTesting
+ boolean wasDeviceIdleForInternal(long ms) {
+ synchronized (mLock) {
+ return mLastUserActivityTime + ms < SystemClock.uptimeMillis();
+ }
+ }
+
+ @VisibleForTesting
+ void onUserActivity() {
+ synchronized (mLock) {
+ mLastUserActivityTime = SystemClock.uptimeMillis();
+ }
+ }
+
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
@@ -4874,5 +4888,10 @@
public void powerHint(int hintId, int data) {
powerHintInternal(hintId, data);
}
+
+ @Override
+ public boolean wasDeviceIdleFor(long ms) {
+ return wasDeviceIdleForInternal(ms);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 79e2688..02689a9 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -29,8 +29,12 @@
import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
import android.os.Temperature;
import android.util.ArrayMap;
import android.util.Slog;
@@ -77,6 +81,10 @@
@GuardedBy("mLock")
private int mStatus;
+ /** If override status takes effect*/
+ @GuardedBy("mLock")
+ private boolean mIsStatusOverride;
+
/** Current thermal map, key as name */
@GuardedBy("mLock")
private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
@@ -184,13 +192,19 @@
newStatus = t.getStatus();
}
}
+ // Do not update if override from shell
+ if (!mIsStatusOverride) {
+ setStatusLocked(newStatus);
+ }
+ }
+
+ private void setStatusLocked(int newStatus) {
if (newStatus != mStatus) {
mStatus = newStatus;
notifyStatusListenersLocked();
}
}
-
private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
@Nullable Integer type) {
synchronized (mLock) {
@@ -236,19 +250,29 @@
}
}
+ private void shutdownIfNeededLocked(Temperature temperature) {
+ if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) {
+ return;
+ }
+ switch (temperature.getType()) {
+ case Temperature.TYPE_CPU:
+ // Fall through
+ case Temperature.TYPE_GPU:
+ // Fall through
+ case Temperature.TYPE_NPU:
+ // Fall through
+ case Temperature.TYPE_SKIN:
+ mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+ break;
+ case Temperature.TYPE_BATTERY:
+ mPowerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
+ break;
+ }
+ }
+
private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
synchronized (mLock) {
- // Thermal Shutdown for Skin temperature
- if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
- && temperature.getType() == Temperature.TYPE_SKIN) {
- final long token = Binder.clearCallingIdentity();
- try {
- mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
+ shutdownIfNeededLocked(temperature);
Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
if (old != null) {
if (old.getStatus() != temperature.getStatus()) {
@@ -263,8 +287,14 @@
}
}
+ /* HwBinder callback **/
private void onTemperatureChangedCallback(Temperature temperature) {
- onTemperatureChanged(temperature, true);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ onTemperatureChanged(temperature, true);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
@@ -285,6 +315,8 @@
final IThermalService.Stub mService = new IThermalService.Stub() {
@Override
public boolean registerThermalEventListener(IThermalEventListener listener) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -305,6 +337,8 @@
@Override
public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
int type) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -324,6 +358,8 @@
@Override
public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -336,6 +372,8 @@
@Override
public List<Temperature> getCurrentTemperatures() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady) {
@@ -349,6 +387,8 @@
@Override
public List<Temperature> getCurrentTemperaturesWithType(int type) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady) {
@@ -393,7 +433,7 @@
}
@Override
- public int getCurrentStatus() {
+ public int getCurrentThermalStatus() {
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -413,14 +453,15 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
+ pw.println("IsStatusOverride: " + mIsStatusOverride);
pw.println("ThermalEventListeners:");
mThermalEventListeners.dump(pw, "\t");
pw.println("ThermalStatusListeners:");
mThermalStatusListeners.dump(pw, "\t");
- pw.println("Thermal Status: " + Integer.toString(mStatus));
+ pw.println("Thermal Status: " + mStatus);
pw.println("Cached temperatures:");
dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
- pw.println("HAL Ready: " + Boolean.toString(mHalReady));
+ pw.println("HAL Ready: " + mHalReady);
if (mHalReady) {
pw.println("HAL connection:");
mHalWrapper.dump(pw, "\t");
@@ -434,8 +475,93 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ private boolean isCallerShell() {
+ final int callingUid = Binder.getCallingUid();
+ return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ if (!isCallerShell()) {
+ Slog.w(TAG, "Only shell is allowed to call thermalservice shell commands");
+ return;
+ }
+ (new ThermalShellCommand()).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
};
+ class ThermalShellCommand extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ switch(cmd != null ? cmd : "") {
+ case "override-status":
+ return runOverrideStatus();
+ case "reset":
+ return runReset();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runReset() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mIsStatusOverride = false;
+ onTemperatureMapChangedLocked();
+ return 0;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private int runOverrideStatus() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final PrintWriter pw = getOutPrintWriter();
+ int status;
+ try {
+ status = Integer.parseInt(getNextArgRequired());
+ } catch (RuntimeException ex) {
+ pw.println("Error: " + ex.toString());
+ return -1;
+ }
+ if (!Temperature.isValidStatus(status)) {
+ pw.println("Invalid status: " + status);
+ return -1;
+ }
+ synchronized (mLock) {
+ mIsStatusOverride = true;
+ setStatusLocked(status);
+ }
+ return 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Thermal service (thermalservice) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" override-status STATUS");
+ pw.println(" sets and locks the thermal status of the device to STATUS.");
+ pw.println(" status code is defined in android.os.Temperature.");
+ pw.println(" reset");
+ pw.println(" unlocks the thermal status of the device.");
+ pw.println();
+ }
+ }
+
abstract static class ThermalHalWrapper {
protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index cb89780..cc3064e 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -34,8 +34,8 @@
import android.rolecontrollerservice.RoleControllerService;
import android.util.Slog;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -48,10 +48,6 @@
static final boolean DEBUG = false;
private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
- // TODO: STOPSHIP: This isn't the right thread, as we are also using it to write to disk.
- @NonNull
- private static final Handler sCallbackHandler = BackgroundThread.getHandler();
-
@NonNull
private final Connection mConnection;
@@ -99,8 +95,8 @@
* @see RoleControllerService#onGrantDefaultRoles(RoleManagerCallback)
*/
public void onGrantDefaultRoles(@NonNull IRoleManagerCallback callback) {
- mConnection.enqueueCall(
- new Connection.Call(IRoleControllerService::onGrantDefaultRoles, callback));
+ mConnection.enqueueCall(new Connection.Call(IRoleControllerService::onGrantDefaultRoles,
+ callback));
}
private static final class Connection implements ServiceConnection {
@@ -113,6 +109,9 @@
@NonNull
private final Context mContext;
+ @NonNull
+ private final Handler mHandler = FgThread.getHandler();
+
private boolean mBound;
@Nullable
@@ -161,8 +160,8 @@
if (DEBUG) {
Slog.i(LOG_TAG, "Enqueue " + call);
}
- sCallbackHandler.executeOrSendMessage(PooledLambda.obtainMessage(
- Connection::executeCall, this, call));
+ mHandler.executeOrSendMessage(PooledLambda.obtainMessage(Connection::executeCall, this,
+ call));
}
@WorkerThread
@@ -181,7 +180,7 @@
@WorkerThread
private void ensureBound() {
- sCallbackHandler.removeCallbacks(mUnbindRunnable);
+ mHandler.removeCallbacks(mUnbindRunnable);
if (!mBound) {
Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
intent.setPackage(mContext.getPackageManager()
@@ -191,13 +190,13 @@
//
// Note that as a result, onServiceConnected may happen not on main thread!
mBound = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
- sCallbackHandler, UserHandle.of(mUserId));
+ mHandler, UserHandle.of(mUserId));
}
}
private void scheduleUnbind() {
- sCallbackHandler.removeCallbacks(mUnbindRunnable);
- sCallbackHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
+ mHandler.removeCallbacks(mUnbindRunnable);
+ mHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
}
@WorkerThread
@@ -220,6 +219,9 @@
private final IRoleManagerCallback mCallback;
@NonNull
+ private final Handler mHandler = FgThread.getHandler();
+
+ @NonNull
private final Runnable mTimeoutRunnable = this::notifyTimeout;
private boolean mCallbackNotified;
@@ -236,7 +238,7 @@
Slog.i(LOG_TAG, "Executing " + this);
}
try {
- sCallbackHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
+ mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
mCallExecutor.execute(service, new CallbackDelegate());
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling RoleControllerService", e);
@@ -256,7 +258,7 @@
return;
}
mCallbackNotified = true;
- sCallbackHandler.removeCallbacks(mTimeoutRunnable);
+ mHandler.removeCallbacks(mTimeoutRunnable);
try {
if (success) {
mCallback.onSuccess();
@@ -286,14 +288,14 @@
@Override
public void onSuccess() throws RemoteException {
- sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
- Call::notifyCallback, Call.this, true));
+ mHandler.sendMessage(PooledLambda.obtainMessage(Call::notifyCallback, Call.this,
+ true));
}
@Override
public void onFailure() throws RemoteException {
- sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
- Call::notifyCallback, Call.this, false));
+ mHandler.sendMessage(PooledLambda.obtainMessage(Call::notifyCallback, Call.this,
+ false));
}
}
}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index b065470..35013de 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -18,11 +18,14 @@
import android.Manifest;
import android.annotation.CheckResult;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManager;
import android.app.role.IRoleManagerCallback;
import android.app.role.RoleManager;
@@ -32,6 +35,9 @@
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.os.Handler;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
@@ -41,20 +47,29 @@
import android.util.PackageUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.FunctionalUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -65,7 +80,7 @@
*
* @see RoleManager
*/
-public class RoleManagerService extends SystemService {
+public class RoleManagerService extends SystemService implements RoleUserState.Callback {
private static final String LOG_TAG = RoleManagerService.class.getSimpleName();
@@ -92,6 +107,17 @@
private final SparseArray<RemoteRoleControllerService> mControllerServices =
new SparseArray<>();
+ /**
+ * Maps user id to its list of listeners.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<RemoteCallbackList<IOnRoleHoldersChangedListener>> mListeners =
+ new SparseArray<>();
+
+ @NonNull
+ private final Handler mListenerHandler = FgThread.getHandler();
+
public RoleManagerService(@NonNull Context context) {
super(context);
@@ -106,7 +132,7 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
getContext().registerReceiverAsUser(new BroadcastReceiver() {
@Override
- public void onReceive(Context context, Intent intent) {
+ public void onReceive(@NonNull Context context, @NonNull Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_USER_REMOVED)) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
onRemoveUser(userId);
@@ -118,33 +144,51 @@
@Override
public void onStart() {
publishBinderService(Context.ROLE_SERVICE, new Stub());
+
//TODO add watch for new user creation and run default grants for them
- //TODO add package update watch to detect PermissionController upgrade and run def. grants
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ getContext().registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1));
+ if (RemoteRoleControllerService.DEBUG) {
+ Slog.i(LOG_TAG,
+ "Packages changed - re-running initial grants for user " + userId);
+ }
+ performInitialGrantsIfNecessary(userId);
+ }
+ }, UserHandle.SYSTEM, intentFilter, null /* broadcastPermission */, null /* handler */);
}
@Override
public void onStartUser(@UserIdInt int userId) {
+ performInitialGrantsIfNecessary(userId);
+ }
+
+ @MainThread
+ private void performInitialGrantsIfNecessary(@UserIdInt int userId) {
RoleUserState userState;
- synchronized (mLock) {
- userState = getUserStateLocked(userId);
- }
+ userState = getOrCreateUserState(userId);
String packagesHash = computeComponentStateHash(userId);
- boolean needGrant;
- synchronized (mLock) {
- needGrant = !packagesHash.equals(userState.getLastGrantPackagesHashLocked());
- }
+ String oldPackagesHash = userState.getPackagesHash();
+ boolean needGrant = !Objects.equals(packagesHash, oldPackagesHash);
if (needGrant) {
// Some vital packages state has changed since last role grant
// Run grants again
Slog.i(LOG_TAG, "Granting default permissions...");
CompletableFuture<Void> result = new CompletableFuture<>();
- getControllerService(userId).onGrantDefaultRoles(
+ getOrCreateControllerService(userId).onGrantDefaultRoles(
new IRoleManagerCallback.Stub() {
@Override
public void onSuccess() {
result.complete(null);
}
-
@Override
public void onFailure() {
result.completeExceptionally(new RuntimeException());
@@ -152,9 +196,7 @@
});
try {
result.get(5, TimeUnit.SECONDS);
- synchronized (mLock) {
- userState.setLastGrantPackagesHashLocked(packagesHash);
- }
+ userState.setPackagesHash(packagesHash);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
}
@@ -163,7 +205,8 @@
}
}
- private String computeComponentStateHash(int userId) {
+ @Nullable
+ private static String computeComponentStateHash(@UserIdInt int userId) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -193,36 +236,91 @@
return PackageUtils.computeSha256Digest(out.toByteArray());
}
- @GuardedBy("mLock")
@NonNull
- private RoleUserState getUserStateLocked(@UserIdInt int userId) {
- RoleUserState userState = mUserStates.get(userId);
- if (userState == null) {
- userState = new RoleUserState(userId);
- userState.readSyncLocked();
- mUserStates.put(userId, userState);
+ private RoleUserState getOrCreateUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ RoleUserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new RoleUserState(userId, this);
+ mUserStates.put(userId, userState);
+ }
+ return userState;
}
- return userState;
}
- @GuardedBy("mLock")
@NonNull
- private RemoteRoleControllerService getControllerService(@UserIdInt int userId) {
- RemoteRoleControllerService controllerService = mControllerServices.get(userId);
- if (controllerService == null) {
- controllerService = new RemoteRoleControllerService(userId, getContext());
- mControllerServices.put(userId, controllerService);
+ private RemoteRoleControllerService getOrCreateControllerService(@UserIdInt int userId) {
+ synchronized (mLock) {
+ RemoteRoleControllerService controllerService = mControllerServices.get(userId);
+ if (controllerService == null) {
+ controllerService = new RemoteRoleControllerService(userId, getContext());
+ mControllerServices.put(userId, controllerService);
+ }
+ return controllerService;
}
- return controllerService;
+ }
+
+ @Nullable
+ private RemoteCallbackList<IOnRoleHoldersChangedListener> getListeners(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mListeners.get(userId);
+ }
+ }
+
+ @NonNull
+ private RemoteCallbackList<IOnRoleHoldersChangedListener> getOrCreateListeners(
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = mListeners.get(userId);
+ if (listeners == null) {
+ listeners = new RemoteCallbackList<>();
+ mListeners.put(userId, listeners);
+ }
+ return listeners;
+ }
}
private void onRemoveUser(@UserIdInt int userId) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners;
+ RoleUserState userState;
synchronized (mLock) {
+ listeners = mListeners.removeReturnOld(userId);
mControllerServices.remove(userId);
- RoleUserState userState = mUserStates.removeReturnOld(userId);
- if (userState != null) {
- userState.destroySyncLocked();
+ userState = mUserStates.removeReturnOld(userId);
+ }
+ if (listeners != null) {
+ listeners.kill();
+ }
+ if (userState != null) {
+ userState.destroy();
+ }
+ }
+
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ mListenerHandler.sendMessage(PooledLambda.obtainMessage(
+ RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId));
+ }
+
+ @WorkerThread
+ private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
+ if (listeners == null) {
+ return;
+ }
+
+ int broadcastCount = listeners.beginBroadcast();
+ try {
+ for (int i = 0; i < broadcastCount; i++) {
+ IOnRoleHoldersChangedListener listener = listeners.getBroadcastItem(i);
+ try {
+ listener.onRoleHoldersChanged(roleName, userId);
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e);
+ }
}
+ } finally {
+ listeners.finishBroadcast();
}
}
@@ -233,10 +331,8 @@
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
int userId = UserHandle.getUserId(getCallingUid());
- synchronized (mLock) {
- RoleUserState userState = getUserStateLocked(userId);
- return userState.isRoleAvailableLocked(roleName);
- }
+ RoleUserState userState = getOrCreateUserState(userId);
+ return userState.isRoleAvailable(roleName);
}
@Override
@@ -276,10 +372,8 @@
@Nullable
private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName,
@UserIdInt int userId) {
- synchronized (mLock) {
- RoleUserState userState = getUserStateLocked(userId);
- return userState.getRoleHoldersLocked(roleName);
- }
+ RoleUserState userState = getOrCreateUserState(userId);
+ return userState.getRoleHolders(roleName);
}
@Override
@@ -296,7 +390,7 @@
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"addRoleHolderAsUser");
- getControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
+ getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
}
@Override
@@ -313,7 +407,7 @@
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"removeRoleHolderAsUser");
- getControllerService(userId).onRemoveRoleHolder(roleName, packageName,
+ getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName,
callback);
}
@@ -330,7 +424,43 @@
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"clearRoleHoldersAsUser");
- getControllerService(userId).onClearRoleHolders(roleName, callback);
+ getOrCreateControllerService(userId).onClearRoleHolders(roleName, callback);
+ }
+
+ @Override
+ public void addOnRoleHoldersChangedListenerAsUser(
+ @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ if (!mUserManagerInternal.exists(userId)) {
+ Slog.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+ userId = handleIncomingUser(userId, "addOnRoleHoldersChangedListenerAsUser");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
+ "addOnRoleHoldersChangedListenerAsUser");
+
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getOrCreateListeners(
+ userId);
+ listeners.register(listener);
+ }
+
+ @Override
+ public void removeOnRoleHoldersChangedListenerAsUser(
+ @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ if (!mUserManagerInternal.exists(userId)) {
+ Slog.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+ userId = handleIncomingUser(userId, "removeOnRoleHoldersChangedListenerAsUser");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
+ "removeOnRoleHoldersChangedListenerAsUser");
+
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
+ if (listener == null) {
+ return;
+ }
+ listeners.unregister(listener);
}
@Override
@@ -341,10 +471,8 @@
"setRoleNamesFromController");
int userId = UserHandle.getCallingUserId();
- synchronized (mLock) {
- RoleUserState userState = getUserStateLocked(userId);
- userState.setRoleNamesLocked(roleNames);
- }
+ RoleUserState userState = getOrCreateUserState(userId);
+ userState.setRoleNames(roleNames);
}
@Override
@@ -357,10 +485,8 @@
"addRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
- synchronized (mLock) {
- RoleUserState userState = getUserStateLocked(userId);
- return userState.addRoleHolderLocked(roleName, packageName);
- }
+ RoleUserState userState = getOrCreateUserState(userId);
+ return userState.addRoleHolder(roleName, packageName);
}
@Override
@@ -373,10 +499,8 @@
"removeRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
- synchronized (mLock) {
- RoleUserState userState = getUserStateLocked(userId);
- return userState.removeRoleHolderLocked(roleName, packageName);
- }
+ RoleUserState userState = getOrCreateUserState(userId);
+ return userState.removeRoleHolder(roleName, packageName);
}
@CheckResult
@@ -386,11 +510,40 @@
}
@Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) {
- (new RoleManagerShellCommand(this)).exec(
- this, in, out, err, args, callback, resultReceiver);
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+ new RoleManagerShellCommand(this).exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), LOG_TAG, fout)) {
+ return;
+ }
+
+ boolean dumpAsProto = args != null && ArrayUtils.contains(args, "--proto");
+ DualDumpOutputStream dumpOutputStream;
+ if (dumpAsProto) {
+ dumpOutputStream = new DualDumpOutputStream(new ProtoOutputStream(fd));
+ } else {
+ fout.println("ROLE MANAGER STATE (dumpsys role):");
+ dumpOutputStream = new DualDumpOutputStream(new IndentingPrintWriter(fout, " "));
+ }
+
+ int[] userIds = mUserManagerInternal.getUserIds();
+ int userIdsLength = userIds.length;
+ for (int i = 0; i < userIdsLength; i++) {
+ int userId = userIds[i];
+
+ RoleUserState userState = getOrCreateUserState(userId);
+ userState.dump(dumpOutputStream, "user_states",
+ RoleManagerServiceDumpProto.USER_STATES);
+ }
+
+ dumpOutputStream.flush();
}
}
}
diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
index e1977ef..336b311 100644
--- a/services/core/java/com/android/server/role/RoleManagerShellCommand.java
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -16,6 +16,8 @@
package com.android.server.role;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.role.IRoleManager;
import android.app.role.IRoleManagerCallback;
import android.os.RemoteException;
@@ -27,13 +29,17 @@
import java.util.concurrent.TimeUnit;
class RoleManagerShellCommand extends ShellCommand {
+
+ @NonNull
private final IRoleManager mRoleManager;
- RoleManagerShellCommand(IRoleManager roleManager) {
+ RoleManagerShellCommand(@NonNull IRoleManager roleManager) {
mRoleManager = roleManager;
}
private class Callback extends IRoleManagerCallback.Stub {
+
+ @NonNull
private final CompletableFuture<Void> mResult = new CompletableFuture<>();
public int waitForResult() {
@@ -58,7 +64,7 @@
}
@Override
- public int onCommand(String cmd) {
+ public int onCommand(@Nullable String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index f218d3a..d55e261 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -32,6 +32,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
import libcore.io.IoUtils;
@@ -47,6 +48,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Objects;
/**
* Stores the state of roles for a user.
@@ -59,6 +61,8 @@
private static final String ROLES_FILE_NAME = "roles.xml";
+ private static final long WRITE_DELAY_MILLIS = 200;
+
private static final String TAG_ROLES = "roles";
private static final String TAG_ROLE = "role";
private static final String TAG_HOLDER = "holder";
@@ -69,35 +73,57 @@
@UserIdInt
private final int mUserId;
- @GuardedBy("RoleManagerService.mLock")
- private int mVersion;
+ @NonNull
+ private final Callback mCallback;
- @GuardedBy("RoleManagerService.mLock")
- private String mLastGrantPackagesHash = null;
+ @NonNull
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private int mVersion = VERSION_UNDEFINED;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private String mPackagesHash;
/**
* Maps role names to its holders' package names. The values should never be null.
*/
- @GuardedBy("RoleManagerService.mLock")
- @Nullable
- private ArrayMap<String, ArraySet<String>> mRoles = null;
+ @GuardedBy("mLock")
+ @NonNull
+ private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
- @GuardedBy("RoleManagerService.mLock")
+ @GuardedBy("mLock")
+ private boolean mWriteScheduled;
+
+ @GuardedBy("mLock")
private boolean mDestroyed;
+ @NonNull
private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
- public RoleUserState(@UserIdInt int userId) {
+ /**
+ * Create a new user state, and read its state from disk if previously persisted.
+ *
+ * @param userId the user id for this user state
+ * @param callback the callback for this user state
+ */
+ public RoleUserState(@UserIdInt int userId, @NonNull Callback callback) {
mUserId = userId;
+ mCallback = callback;
+
+ readFile();
}
/**
* Get the version of this user state.
*/
- @GuardedBy("RoleManagerService.mLock")
- public int getVersionLocked() {
- throwIfDestroyedLocked();
- return mVersion;
+ public int getVersion() {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ return mVersion;
+ }
}
/**
@@ -105,31 +131,45 @@
*
* @param version the version to set
*/
- @GuardedBy("RoleManagerService.mLock")
- public void setVersionLocked(int version) {
- throwIfDestroyedLocked();
- if (mVersion == version) {
- return;
+ public void setVersion(int version) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ if (mVersion == version) {
+ return;
+ }
+ mVersion = version;
+ scheduleWriteFileLocked();
}
- mVersion = version;
- writeAsyncLocked();
}
/**
- * Get the hash representing the state of packages during the last time initial grants was run
+ * Get the hash representing the state of packages during the last time initial grants was run.
+ *
+ * @return the hash representing the state of packages
*/
- @GuardedBy("RoleManagerService.mLock")
- public String getLastGrantPackagesHashLocked() {
- return mLastGrantPackagesHash;
+ @Nullable
+ public String getPackagesHash() {
+ synchronized (mLock) {
+ return mPackagesHash;
+ }
}
/**
- * Set the hash representing the state of packages during the last time initial grants was run
+ * Set the hash representing the state of packages during the last time initial grants was run.
+ *
+ * @param packagesHash the hash representing the state of packages
*/
- @GuardedBy("RoleManagerService.mLock")
- public void setLastGrantPackagesHashLocked(String lastGrantPackagesHash) {
- mLastGrantPackagesHash = lastGrantPackagesHash;
- writeAsyncLocked();
+ public void setPackagesHash(@Nullable String packagesHash) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ if (Objects.equals(mPackagesHash, packagesHash)) {
+ return;
+ }
+ mPackagesHash = packagesHash;
+ scheduleWriteFileLocked();
+ }
}
/**
@@ -139,10 +179,12 @@
*
* @return whether the role is available
*/
- @GuardedBy("RoleManagerService.mLock")
- public boolean isRoleAvailableLocked(@NonNull String roleName) {
- throwIfDestroyedLocked();
- return mRoles.containsKey(roleName);
+ public boolean isRoleAvailable(@NonNull String roleName) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ return mRoles.containsKey(roleName);
+ }
}
/**
@@ -152,11 +194,13 @@
*
* @return the set of role holders. {@code null} should not be returned and indicates an issue.
*/
- @GuardedBy("RoleManagerService.mLock")
@Nullable
- public ArraySet<String> getRoleHoldersLocked(@NonNull String roleName) {
- throwIfDestroyedLocked();
- return mRoles.get(roleName);
+ public ArraySet<String> getRoleHolders(@NonNull String roleName) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ return new ArraySet<>(mRoles.get(roleName));
+ }
}
/**
@@ -164,33 +208,40 @@
*
* @param roleNames the names of all the available roles
*/
- @GuardedBy("RoleManagerService.mLock")
- public void setRoleNamesLocked(@NonNull List<String> roleNames) {
- throwIfDestroyedLocked();
- boolean changed = false;
- for (int i = mRoles.size() - 1; i >= 0; i--) {
- String roleName = mRoles.keyAt(i);
- if (!roleNames.contains(roleName)) {
- ArraySet<String> packageNames = mRoles.valueAt(i);
- if (!packageNames.isEmpty()) {
- Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up, role: "
- + roleName + ", holders: " + packageNames);
+ public void setRoleNames(@NonNull List<String> roleNames) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ boolean changed = false;
+
+ for (int i = mRoles.size() - 1; i >= 0; i--) {
+ String roleName = mRoles.keyAt(i);
+
+ if (!roleNames.contains(roleName)) {
+ ArraySet<String> packageNames = mRoles.valueAt(i);
+ if (!packageNames.isEmpty()) {
+ Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up,"
+ + " role: " + roleName + ", holders: " + packageNames);
+ }
+ mRoles.removeAt(i);
+ changed = true;
}
- mRoles.removeAt(i);
- changed = true;
}
- }
- int roleNamesSize = roleNames.size();
- for (int i = 0; i < roleNamesSize; i++) {
- String roleName = roleNames.get(i);
- if (!mRoles.containsKey(roleName)) {
- mRoles.put(roleName, new ArraySet<>());
- Slog.i(LOG_TAG, "Added new role: " + roleName);
- changed = true;
+
+ int roleNamesSize = roleNames.size();
+ for (int i = 0; i < roleNamesSize; i++) {
+ String roleName = roleNames.get(i);
+
+ if (!mRoles.containsKey(roleName)) {
+ mRoles.put(roleName, new ArraySet<>());
+ Slog.i(LOG_TAG, "Added new role: " + roleName);
+ changed = true;
+ }
}
- }
- if (changed) {
- writeAsyncLocked();
+
+ if (changed) {
+ scheduleWriteFileLocked();
+ }
}
}
@@ -204,18 +255,26 @@
* indicates an issue.
*/
@CheckResult
- @GuardedBy("RoleManagerService.mLock")
- public boolean addRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
- throwIfDestroyedLocked();
- ArraySet<String> roleHolders = mRoles.get(roleName);
- if (roleHolders == null) {
- Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
- + ", package: " + packageName);
- return false;
+ public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+ boolean changed;
+
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ ArraySet<String> roleHolders = mRoles.get(roleName);
+ if (roleHolders == null) {
+ Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
+ + ", package: " + packageName);
+ return false;
+ }
+ changed = roleHolders.add(packageName);
+ if (changed) {
+ scheduleWriteFileLocked();
+ }
}
- boolean changed = roleHolders.add(packageName);
+
if (changed) {
- writeAsyncLocked();
+ mCallback.onRoleHoldersChanged(roleName, mUserId);
}
return true;
}
@@ -230,18 +289,27 @@
* indicates an issue.
*/
@CheckResult
- @GuardedBy("RoleManagerService.mLock")
- public boolean removeRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
- throwIfDestroyedLocked();
- ArraySet<String> roleHolders = mRoles.get(roleName);
- if (roleHolders == null) {
- Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
- + ", package: " + packageName);
- return false;
+ public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+ boolean changed;
+
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ ArraySet<String> roleHolders = mRoles.get(roleName);
+ if (roleHolders == null) {
+ Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
+ + ", package: " + packageName);
+ return false;
+ }
+
+ changed = roleHolders.remove(packageName);
+ if (changed) {
+ scheduleWriteFileLocked();
+ }
}
- boolean changed = roleHolders.remove(packageName);
+
if (changed) {
- writeAsyncLocked();
+ mCallback.onRoleHoldersChanged(roleName, mUserId);
}
return true;
}
@@ -249,26 +317,34 @@
/**
* Schedule writing the state to file.
*/
- @GuardedBy("RoleManagerService.mLock")
- void writeAsyncLocked() {
+ @GuardedBy("mLock")
+ private void scheduleWriteFileLocked() {
throwIfDestroyedLocked();
- int version = mVersion;
- ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
- for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
- String roleName = mRoles.keyAt(i);
- ArraySet<String> roleHolders = mRoles.valueAt(i);
- roleHolders = new ArraySet<>(roleHolders);
- roles.put(roleName, roleHolders);
+
+ if (!mWriteScheduled) {
+ mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeFile,
+ this), WRITE_DELAY_MILLIS);
+ mWriteScheduled = true;
}
- mWriteHandler.removeCallbacksAndMessages(null);
- // TODO: Throttle writes.
- mWriteHandler.sendMessage(PooledLambda.obtainMessage(
- RoleUserState::writeSync, this, version, roles, mLastGrantPackagesHash));
}
@WorkerThread
- private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles,
- String packagesHash) {
+ private void writeFile() {
+ int version;
+ String packagesHash;
+ ArrayMap<String, ArraySet<String>> roles;
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+
+ mWriteScheduled = false;
+
+ version = mVersion;
+ packagesHash = mPackagesHash;
+ roles = snapshotRolesLocked();
+ }
+
AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
FileOutputStream out = null;
try {
@@ -280,10 +356,11 @@
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startDocument(null, true);
- serializeRoles(serializer, version, roles, packagesHash);
+ serializeRoles(serializer, version, packagesHash, roles);
serializer.endDocument();
atomicFile.finishWrite(out);
+ Slog.i(LOG_TAG, "Wrote roles.xml successfully");
} catch (IllegalArgumentException | IllegalStateException | IOException e) {
Slog.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup", e);
if (out != null) {
@@ -296,19 +373,26 @@
@WorkerThread
private void serializeRoles(@NonNull XmlSerializer serializer, int version,
- @NonNull ArrayMap<String, ArraySet<String>> roles, String packagesHash)
+ @Nullable String packagesHash, @NonNull ArrayMap<String, ArraySet<String>> roles)
throws IOException {
serializer.startTag(null, TAG_ROLES);
+
serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
- serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
+
+ if (packagesHash != null) {
+ serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
+ }
+
for (int i = 0, size = roles.size(); i < size; ++i) {
String roleName = roles.keyAt(i);
ArraySet<String> roleHolders = roles.valueAt(i);
+
serializer.startTag(null, TAG_ROLE);
serializer.attribute(null, ATTRIBUTE_NAME, roleName);
serializeRoleHolders(serializer, roleHolders);
serializer.endTag(null, TAG_ROLE);
}
+
serializer.endTag(null, TAG_ROLES);
}
@@ -317,6 +401,7 @@
@NonNull ArraySet<String> roleHolders) throws IOException {
for (int i = 0, size = roleHolders.size(); i < size; ++i) {
String roleHolder = roleHolders.valueAt(i);
+
serializer.startTag(null, TAG_HOLDER);
serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
serializer.endTag(null, TAG_HOLDER);
@@ -326,23 +411,19 @@
/**
* Read the state from file.
*/
- @GuardedBy("RoleManagerService.mLock")
- public void readSyncLocked() {
- if (mRoles != null) {
- throw new IllegalStateException("This RoleUserState has already read the roles.xml");
- }
-
- File file = getFile(mUserId);
- try (FileInputStream in = new AtomicFile(file).openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, null);
- parseXmlLocked(parser);
- } catch (FileNotFoundException e) {
- Slog.i(LOG_TAG, "roles.xml not found");
- mRoles = new ArrayMap<>();
- mVersion = VERSION_UNDEFINED;
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
+ private void readFile() {
+ synchronized (mLock) {
+ File file = getFile(mUserId);
+ try (FileInputStream in = new AtomicFile(file).openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseXmlLocked(parser);
+ Slog.i(LOG_TAG, "Read roles.xml successfully");
+ } catch (FileNotFoundException e) {
+ Slog.i(LOG_TAG, "roles.xml not found");
+ } catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
+ }
}
}
@@ -362,13 +443,14 @@
return;
}
}
+ Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
}
private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
XmlPullParserException {
mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
- mLastGrantPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
- mRoles = new ArrayMap<>();
+ mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
+ mRoles.clear();
int type;
int depth;
@@ -411,18 +493,76 @@
}
/**
- * Destroy this state and delete the corresponding file. Any pending writes to the file will be
- * cancelled and any future interaction with this state will throw an exception.
+ * Dump this user state.
+ *
+ * @param dumpOutputStream the output stream to dump to
*/
- @GuardedBy("RoleManagerService.mLock")
- public void destroySyncLocked() {
- throwIfDestroyedLocked();
- mWriteHandler.removeCallbacksAndMessages(null);
- getFile(mUserId).delete();
- mDestroyed = true;
+ public void dump(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName,
+ long fieldId) {
+ int version;
+ String packagesHash;
+ ArrayMap<String, ArraySet<String>> roles;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ version = mVersion;
+ packagesHash = mPackagesHash;
+ roles = snapshotRolesLocked();
+ }
+
+ long fieldToken = dumpOutputStream.start(fieldName, fieldId);
+ dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId);
+ dumpOutputStream.write("version", RoleUserStateProto.VERSION, version);
+ dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, packagesHash);
+
+ int rolesSize = roles.size();
+ for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
+ String roleName = roles.keyAt(rolesIndex);
+ ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
+
+ long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
+ dumpOutputStream.write("name", RoleProto.NAME, roleName);
+
+ int roleHoldersSize = roleHolders.size();
+ for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) {
+ String roleHolder = roleHolders.valueAt(roleHoldersIndex);
+
+ dumpOutputStream.write("holders", RoleProto.HOLDERS, roleHolder);
+ }
+
+ dumpOutputStream.end(rolesToken);
+ }
+
+ dumpOutputStream.end(fieldToken);
}
- @GuardedBy("RoleManagerService.mLock")
+ @GuardedBy("mLock")
+ private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
+ ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
+ for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
+ String roleName = mRoles.keyAt(i);
+ ArraySet<String> roleHolders = mRoles.valueAt(i);
+
+ roleHolders = new ArraySet<>(roleHolders);
+ roles.put(roleName, roleHolders);
+ }
+ return roles;
+ }
+
+ /**
+ * Destroy this user state and delete the corresponding file. Any pending writes to the file
+ * will be cancelled, and any future interaction with this state will throw an exception.
+ */
+ public void destroy() {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ mWriteHandler.removeCallbacksAndMessages(null);
+ getFile(mUserId).delete();
+ mDestroyed = true;
+ }
+ }
+
+ @GuardedBy("mLock")
private void throwIfDestroyedLocked() {
if (mDestroyed) {
throw new IllegalStateException("This RoleUserState has already been destroyed");
@@ -432,4 +572,18 @@
private static @NonNull File getFile(@UserIdInt int userId) {
return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
}
+
+ /**
+ * Callback for a user state.
+ */
+ public interface Callback {
+
+ /**
+ * Called when the holders of roles are changed.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param userId the user id for this role holder change
+ */
+ void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
+ }
}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/services/core/java/com/android/server/signedconfig/InvalidConfigException.java
similarity index 61%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to services/core/java/com/android/server/signedconfig/InvalidConfigException.java
index c66a6cb..f01baa4 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/services/core/java/com/android/server/signedconfig/InvalidConfigException.java
@@ -14,6 +14,20 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package com.android.server.signedconfig;
-parcelable ContentCaptureEvent;
+/**
+ * Thrown when there is a problem parsing the config embedded in an APK.
+ */
+public class InvalidConfigException extends Exception {
+
+ public InvalidConfigException(String message) {
+ super(message);
+ }
+
+ public InvalidConfigException(String message, Exception cause) {
+ super(message, cause);
+ }
+
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
new file mode 100644
index 0000000..944db84
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import android.os.Build;
+import android.util.Slog;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * Helper class for verifying config signatures.
+ */
+public class SignatureVerifier {
+
+ private static final String TAG = "SignedConfig";
+ private static final boolean DBG = false;
+
+ private static final String DEBUG_KEY =
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60"
+ + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==";
+
+ private final PublicKey mDebugKey;
+
+ public SignatureVerifier() {
+ mDebugKey = createKey(DEBUG_KEY);
+ }
+
+ private static PublicKey createKey(String base64) {
+ EncodedKeySpec keySpec;
+ try {
+ byte[] key = Base64.getDecoder().decode(base64);
+ keySpec = new X509EncodedKeySpec(key);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to base64 decode public key", e);
+ return null;
+ }
+ try {
+ KeyFactory factory = KeyFactory.getInstance("EC");
+ return factory.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ Slog.e(TAG, "Failed to construct public key", e);
+ return null;
+ }
+ }
+
+ /**
+ * Verify a signature for signed config.
+ *
+ * @param config Config as read from APK meta-data.
+ * @param base64Signature Signature as read from APK meta-data.
+ * @return {@code true} iff the signature was successfully verified.
+ */
+ public boolean verifySignature(String config, String base64Signature)
+ throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ byte[] signature;
+ try {
+ signature = Base64.getDecoder().decode(base64Signature);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to base64 decode signature");
+ return false;
+ }
+ byte[] data = config.getBytes(StandardCharsets.UTF_8);
+ if (DBG) Slog.i(TAG, "Data: " + Base64.getEncoder().encodeToString(data));
+
+ if (Build.IS_DEBUGGABLE) {
+ if (mDebugKey != null) {
+ if (DBG) Slog.w(TAG, "Trying to verify signature using debug key");
+ Signature verifier = Signature.getInstance("SHA256withECDSA");
+ verifier.initVerify(mDebugKey);
+ verifier.update(data);
+ if (verifier.verify(signature)) {
+ Slog.i(TAG, "Verified config using debug key");
+ return true;
+ } else {
+ if (DBG) Slog.i(TAG, "Config verification failed using debug key");
+ }
+ } else {
+ Slog.w(TAG, "Debuggable build, but have no debug key");
+ }
+ }
+ // TODO verify production key.
+ Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION");
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfig.java b/services/core/java/com/android/server/signedconfig/SignedConfig.java
new file mode 100644
index 0000000..560a1e1
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfig.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents signed configuration.
+ *
+ * <p>This configuration should only be used if the signature has already been verified.
+ *
+ * This class also parses signed config from JSON. The format expected is:
+ * <pre>
+ * {
+ * "version": 1
+ * "config": [
+ * {
+ * "min_sdk": 28,
+ * "max_sdk": 29,
+ * "values": {
+ * "key": "value",
+ * "key2": "value2"
+ * ...
+ * }
+ * },
+ * ...
+ * ],
+ * }
+ * </pre>
+ */
+public class SignedConfig {
+
+ private static final String KEY_VERSION = "version";
+ private static final String KEY_CONFIG = "config";
+
+ private static final String CONFIG_KEY_MIN_SDK = "min_sdk";
+ private static final String CONFIG_KEY_MAX_SDK = "max_sdk";
+ private static final String CONFIG_KEY_VALUES = "values";
+
+ /**
+ * Represents config values targeting an SDK range.
+ */
+ public static class PerSdkConfig {
+ public final int minSdk;
+ public final int maxSdk;
+ public final Map<String, String> values;
+
+ public PerSdkConfig(int minSdk, int maxSdk, Map<String, String> values) {
+ this.minSdk = minSdk;
+ this.maxSdk = maxSdk;
+ this.values = Collections.unmodifiableMap(values);
+ }
+
+ }
+
+ public final int version;
+ public final List<PerSdkConfig> perSdkConfig;
+
+ public SignedConfig(int version, List<PerSdkConfig> perSdkConfig) {
+ this.version = version;
+ this.perSdkConfig = Collections.unmodifiableList(perSdkConfig);
+ }
+
+ /**
+ * Find matching sdk config for a given SDK level.
+ *
+ * @param sdkVersion SDK version of device.
+ * @return Matching config, of {@code null} if there is none.
+ */
+ public PerSdkConfig getMatchingConfig(int sdkVersion) {
+ for (PerSdkConfig config : perSdkConfig) {
+ if (config.minSdk <= sdkVersion && sdkVersion <= config.maxSdk) {
+ return config;
+ }
+ }
+ // nothing matching
+ return null;
+ }
+
+ /**
+ * Parse configuration from an APK.
+ *
+ * @param config Config string as read from the APK metadata.
+ * @param allowedKeys Set of allowed keys in the config. Any key/value mapping for a key not in
+ * this set will result in an {@link InvalidConfigException} being thrown.
+ * @param keyValueMappers Mappings for values per key. The keys in the top level map should be
+ * a subset of {@code allowedKeys}. The keys in the inner map indicate
+ * the set of allowed values for that keys value. This map will be
+ * applied to the value in the configuration. This is intended to allow
+ * enum-like values to be encoded as strings in the configuration, and
+ * mapped back to integers when the configuration is parsed.
+ *
+ * <p>Any config key with a value that does not appear in the
+ * corresponding map will result in an {@link InvalidConfigException}
+ * being thrown.
+ * @return Parsed configuration.
+ * @throws InvalidConfigException If there's a problem parsing the config.
+ */
+ public static SignedConfig parse(String config, Set<String> allowedKeys,
+ Map<String, Map<String, String>> keyValueMappers)
+ throws InvalidConfigException {
+ try {
+ JSONObject json = new JSONObject(config);
+ int version = json.getInt(KEY_VERSION);
+
+ JSONArray perSdkConfig = json.getJSONArray(KEY_CONFIG);
+ List<PerSdkConfig> parsedConfigs = new ArrayList<>();
+ for (int i = 0; i < perSdkConfig.length(); ++i) {
+ parsedConfigs.add(parsePerSdkConfig(perSdkConfig.getJSONObject(i), allowedKeys,
+ keyValueMappers));
+ }
+
+ return new SignedConfig(version, parsedConfigs);
+ } catch (JSONException e) {
+ throw new InvalidConfigException("Could not parse JSON", e);
+ }
+
+ }
+
+ private static CharSequence quoted(Object s) {
+ if (s == null) {
+ return "null";
+ } else {
+ return "\"" + s + "\"";
+ }
+ }
+
+ @VisibleForTesting
+ static PerSdkConfig parsePerSdkConfig(JSONObject json, Set<String> allowedKeys,
+ Map<String, Map<String, String>> keyValueMappers)
+ throws JSONException, InvalidConfigException {
+ int minSdk = json.getInt(CONFIG_KEY_MIN_SDK);
+ int maxSdk = json.getInt(CONFIG_KEY_MAX_SDK);
+ JSONObject valuesJson = json.getJSONObject(CONFIG_KEY_VALUES);
+ Map<String, String> values = new HashMap<>();
+ for (String key : valuesJson.keySet()) {
+ Object valueObject = valuesJson.get(key);
+ String value = valueObject == JSONObject.NULL || valueObject == null
+ ? null
+ : valueObject.toString();
+ if (!allowedKeys.contains(key)) {
+ throw new InvalidConfigException("Config key " + key + " is not allowed");
+ }
+ if (keyValueMappers.containsKey(key)) {
+ Map<String, String> mapper = keyValueMappers.get(key);
+ if (!mapper.containsKey(value)) {
+ throw new InvalidConfigException(
+ "Config key " + key + " contains unsupported value " + quoted(value));
+ }
+ value = mapper.get(value);
+ }
+ values.put(key, value);
+ }
+ return new PerSdkConfig(minSdk, maxSdk, values);
+ }
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
new file mode 100644
index 0000000..4908964
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+class SignedConfigApplicator {
+
+ private static final String TAG = "SignedConfig";
+
+ private static final Set<String> ALLOWED_KEYS = Collections.unmodifiableSet(new ArraySet<>(
+ Arrays.asList(
+ Settings.Global.HIDDEN_API_POLICY,
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS
+ )));
+
+ private static final Map<String, String> HIDDEN_API_POLICY_KEY_MAP = makeMap(
+ "DEFAULT", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT),
+ "DISABLED", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED),
+ "JUST_WARN", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_JUST_WARN),
+ "ENABLED", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED)
+ );
+
+ private static final Map<String, Map<String, String>> KEY_VALUE_MAPPERS = makeMap(
+ Settings.Global.HIDDEN_API_POLICY, HIDDEN_API_POLICY_KEY_MAP
+ );
+
+ private static <K, V> Map<K, V> makeMap(Object... keyValuePairs) {
+ if (keyValuePairs.length % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ final int len = keyValuePairs.length / 2;
+ ArrayMap<K, V> m = new ArrayMap<>(len);
+ for (int i = 0; i < len; ++i) {
+ m.put((K) keyValuePairs[i * 2], (V) keyValuePairs[(i * 2) + 1]);
+ }
+ return Collections.unmodifiableMap(m);
+
+ }
+
+ private final Context mContext;
+ private final String mSourcePackage;
+ private final SignatureVerifier mVerifier;
+
+ SignedConfigApplicator(Context context, String sourcePackage) {
+ mContext = context;
+ mSourcePackage = sourcePackage;
+ mVerifier = new SignatureVerifier();
+ }
+
+ private boolean checkSignature(String data, String signature) {
+ try {
+ return mVerifier.verifySignature(data, signature);
+ } catch (GeneralSecurityException e) {
+ Slog.e(TAG, "Failed to verify signature", e);
+ return false;
+ }
+ }
+
+ private int getCurrentConfigVersion() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SIGNED_CONFIG_VERSION, 0);
+ }
+
+ private void updateCurrentConfig(int version, Map<String, String> values) {
+ for (Map.Entry<String, String> e: values.entrySet()) {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ e.getKey(),
+ e.getValue());
+ }
+ Settings.Global.putInt(
+ mContext.getContentResolver(), Settings.Global.SIGNED_CONFIG_VERSION, version);
+ }
+
+
+ void applyConfig(String configStr, String signature) {
+ if (!checkSignature(configStr, signature)) {
+ Slog.e(TAG, "Signature check on signed configuration in package " + mSourcePackage
+ + " failed; ignoring");
+ return;
+ }
+ SignedConfig config;
+ try {
+ config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
+ } catch (InvalidConfigException e) {
+ Slog.e(TAG, "Failed to parse config from package " + mSourcePackage, e);
+ return;
+ }
+ int currentVersion = getCurrentConfigVersion();
+ if (currentVersion >= config.version) {
+ Slog.i(TAG, "Config from package " + mSourcePackage + " is older than existing: "
+ + config.version + "<=" + currentVersion);
+ return;
+ }
+ // We have new config!
+ Slog.i(TAG, "Got new signed config from package " + mSourcePackage + ": version "
+ + config.version + " replacing existing version " + currentVersion);
+ SignedConfig.PerSdkConfig matchedConfig =
+ config.getMatchingConfig(Build.VERSION.SDK_INT);
+ if (matchedConfig == null) {
+ Slog.i(TAG, "Config is not applicable to current SDK version; ignoring");
+ return;
+ }
+
+ Slog.i(TAG, "Updating signed config to version " + config.version);
+ updateCurrentConfig(config.version, matchedConfig.values);
+ }
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
new file mode 100644
index 0000000..84ce93f
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+/**
+ * Signed config service. This is not an Android Service, but just owns a broadcast receiver for
+ * receiving package install and update notifications from the package manager.
+ */
+public class SignedConfigService {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "SignedConfig";
+
+ // TODO should these be elsewhere? In a public API?
+ private static final String KEY_CONFIG = "android.signedconfig";
+ private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature";
+
+ private static class UpdateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ new SignedConfigService(context).handlePackageBroadcast(intent);
+ }
+ }
+
+ private final Context mContext;
+ private final PackageManagerInternal mPacMan;
+
+ public SignedConfigService(Context context) {
+ mContext = context;
+ mPacMan = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ void handlePackageBroadcast(Intent intent) {
+ if (DBG) Slog.d(TAG, "handlePackageBroadcast " + intent);
+ Uri packageData = intent.getData();
+ String packageName = packageData == null ? null : packageData.getSchemeSpecificPart();
+ if (DBG) Slog.d(TAG, "handlePackageBroadcast package=" + packageName);
+ if (packageName == null) {
+ return;
+ }
+ int userId = mContext.getUser().getIdentifier();
+ PackageInfo pi = mPacMan.getPackageInfo(packageName, PackageManager.GET_META_DATA,
+ android.os.Process.SYSTEM_UID, userId);
+ if (pi == null) {
+ Slog.w(TAG, "Got null PackageInfo for " + packageName + "; user " + userId);
+ return;
+ }
+ Bundle metaData = pi.applicationInfo.metaData;
+ if (metaData == null) {
+ if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata");
+ return;
+ }
+ if (metaData.containsKey(KEY_CONFIG)
+ && metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
+ String config = metaData.getString(KEY_CONFIG);
+ String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+ try {
+ // Base64 encoding is standard (not URL safe) encoding: RFC4648
+ config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed to base64 decode config from " + packageName);
+ return;
+ }
+ if (DBG) {
+ Slog.d(TAG, "Got signed config: " + config);
+ Slog.d(TAG, "Got config signature: " + signature);
+ }
+ new SignedConfigApplicator(mContext, packageName).applyConfig(
+ config, signature);
+ } else {
+ if (DBG) Slog.d(TAG, "Package has no config/signature.");
+ }
+ }
+
+ /**
+ * Register to receive broadcasts from the package manager.
+ */
+ public static void registerUpdateReceiver(Context context) {
+ if (DBG) Slog.d(TAG, "Registering receiver");
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addDataScheme("package");
+ context.registerReceiver(new UpdateReceiver(), filter);
+ }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index e645b84..6e4c00e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.statusbar;
import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityThread;
import android.app.Notification;
@@ -25,6 +26,8 @@
import android.content.Context;
import android.graphics.Rect;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -40,7 +43,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import android.view.Display;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.statusbar.IStatusBar;
@@ -63,7 +66,7 @@
* A note on locking: We rely on the fact that calls onto mBar are oneway or
* if they are local, that they just enqueue messages to not deadlock.
*/
-public class StatusBarManagerService extends IStatusBarService.Stub {
+public class StatusBarManagerService extends IStatusBarService.Stub implements DisplayListener {
private static final String TAG = "StatusBarManagerService";
private static final boolean SPEW = false;
@@ -79,24 +82,13 @@
private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
private GlobalActionsProvider.GlobalActionsListener mGlobalActionListener;
private IBinder mSysUiVisToken = new Binder();
- private int mDisabled1 = 0;
- private int mDisabled2 = 0;
private final Object mLock = new Object();
private final DeathRecipient mDeathRecipient = new DeathRecipient();
- // encompasses lights-out mode and other flags defined on View
- private int mSystemUiVisibility = 0;
- private int mFullscreenStackSysUiVisibility;
- private int mDockedStackSysUiVisibility;
- private final Rect mFullscreenStackBounds = new Rect();
- private final Rect mDockedStackBounds = new Rect();
- private boolean mMenuVisible = false;
- private int mImeWindowVis = 0;
- private int mImeBackDisposition;
- private boolean mShowImeSwitcher;
- private IBinder mImeToken = null;
private int mCurrentUserId;
+ private SparseArray<UiState> mDisplayUiState = new SparseArray<>();
+
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
mBar.asBinder().unlinkToDeath(this,0);
@@ -185,8 +177,29 @@
LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
+
+ // We always have a default display.
+ final UiState state = new UiState();
+ mDisplayUiState.put(DEFAULT_DISPLAY, state);
+
+ final DisplayManager displayManager =
+ (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ displayManager.registerDisplayListener(this, mHandler);
}
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mDisplayUiState.remove(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {}
+
/**
* Private API used by NotificationManagerService.
*/
@@ -240,22 +253,14 @@
@Override
public void topAppWindowChanged(int displayId, boolean menuVisible) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
- StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+ StatusBarManagerService.this.topAppWindowChanged(displayId, menuVisible);
}
@Override
public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
String cause) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
- StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+ StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
}
@@ -272,13 +277,9 @@
@Override
public void appTransitionFinished(int displayId) {
enforceStatusBarService();
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.appTransitionFinished();
+ mBar.appTransitionFinished(displayId);
} catch (RemoteException ex) {}
}
}
@@ -374,39 +375,27 @@
@Override
public void setWindowState(int displayId, int window, int state) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.setWindowState(window, state);
+ mBar.setWindowState(displayId, window, state);
} catch (RemoteException ex) {}
}
}
@Override
public void appTransitionPending(int displayId) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.appTransitionPending();
+ mBar.appTransitionPending(displayId);
} catch (RemoteException ex) {}
}
}
@Override
public void appTransitionCancelled(int displayId) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.appTransitionCancelled();
+ mBar.appTransitionCancelled(displayId);
} catch (RemoteException ex) {}
}
}
@@ -414,14 +403,10 @@
@Override
public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
long statusBarAnimationsDuration) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
mBar.appTransitionStarting(
- statusBarAnimationsStartTime, statusBarAnimationsDuration);
+ displayId, statusBarAnimationsStartTime, statusBarAnimationsDuration);
} catch (RemoteException ex) {}
}
}
@@ -449,6 +434,7 @@
return false;
}
+ // TODO(b/118592525): support it per display if necessary.
@Override
public void onProposedRotationChanged(int rotation, boolean isValid) {
if (mBar != null){
@@ -462,7 +448,9 @@
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
@Override
public boolean isGlobalActionsDisabled() {
- return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+ // TODO(b/118592525): support global actions for multi-display.
+ final int disabled2 = mDisplayUiState.get(DEFAULT_DISPLAY).getDisabled2();
+ return (disabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
}
@Override
@@ -664,20 +652,23 @@
}
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
@Override
public void disable(int what, IBinder token, String pkg) {
disableForUser(what, token, pkg, mCurrentUserId);
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
@Override
public void disableForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
synchronized (mLock) {
- disableLocked(userId, what, token, pkg, 1);
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
}
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
/**
* Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
* To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -689,6 +680,7 @@
disable2ForUser(what, token, pkg, mCurrentUserId);
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
/**
* Disable additional status bar features for a given user. Pass the bitwise-or of the
* DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -700,11 +692,12 @@
enforceStatusBar();
synchronized (mLock) {
- disableLocked(userId, what, token, pkg, 2);
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
}
}
- private void disableLocked(int userId, int what, IBinder token, String pkg, int whichFlag) {
+ private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
+ int whichFlag) {
// It's important that the the callback and the call to mBar get done
// in the same order when multiple threads are calling this function
// so they are paired correctly. The messages on the handler will be
@@ -723,22 +716,19 @@
disabledData += " ([" + i + "] " + tok + "), ";
}
disabledData += " }";
- Log.d(TAG, "disabledlocked (b/113914868): net1=" + net1 + ", mDisabled1=" + mDisabled1
- + ", token=" + token + ", mDisableRecords=" + mDisableRecords.size() + " => "
- + disabledData);
- }
+ final UiState state = getUiState(displayId);
- if (net1 != mDisabled1 || net2 != mDisabled2) {
- mDisabled1 = net1;
- mDisabled2 = net2;
- mHandler.post(new Runnable() {
- public void run() {
- mNotificationDelegate.onSetDisabled(net1);
- }
- });
+ Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+ + ", mDisabled1=" + state.mDisabled1 + ", token=" + token
+ + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
+ }
+ final UiState state = getUiState(displayId);
+ if (state.disableEquals(net1, net2)) {
+ state.setDisabled(net1, net2);
+ mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
if (mBar != null) {
try {
- mBar.disable(net1, net2);
+ mBar.disable(displayId, net1, net2);
} catch (RemoteException ex) {
}
}
@@ -808,26 +798,27 @@
* response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
* to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
*/
- private void topAppWindowChanged(final boolean menuVisible) {
+ private void topAppWindowChanged(int displayId, final boolean menuVisible) {
enforceStatusBar();
- if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
-
+ if (SPEW) {
+ Slog.d(TAG, "display#" + displayId + ": "
+ + (menuVisible ? "showing" : "hiding") + " MENU key");
+ }
synchronized(mLock) {
- mMenuVisible = menuVisible;
- mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.topAppWindowChanged(menuVisible);
- } catch (RemoteException ex) {
- }
+ getUiState(displayId).setMenuVisible(menuVisible);
+ mHandler.post(() -> {
+ if (mBar != null) {
+ try {
+ mBar.topAppWindowChanged(displayId, menuVisible);
+ } catch (RemoteException ex) {
}
}
});
}
}
+ // TODO(b/117478341): support back button change when IME is showing on a external display.
@Override
public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
final boolean showImeSwitcher) {
@@ -841,39 +832,42 @@
// In case of IME change, we need to call up setImeWindowStatus() regardless of
// mImeWindowVis because mImeWindowVis may not have been set to false when the
// previous IME was destroyed.
- mImeWindowVis = vis;
- mImeBackDisposition = backDisposition;
- mImeToken = token;
- mShowImeSwitcher = showImeSwitcher;
- mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
- } catch (RemoteException ex) {
- }
- }
- }
+ // TODO(b/117478341): support back button change when IME is showing on a external
+ // display.
+ getUiState(DEFAULT_DISPLAY)
+ .setImeWindowState(vis, backDisposition, showImeSwitcher, token);
+
+ mHandler.post(() -> {
+ if (mBar == null) return;
+ try {
+ // TODO(b/117478341): support back button change when IME is showing on a
+ // external display.
+ mBar.setImeWindowStatus(
+ DEFAULT_DISPLAY, token, vis, backDisposition, showImeSwitcher);
+ } catch (RemoteException ex) { }
});
}
}
@Override
- public void setSystemUiVisibility(int vis, int mask, String cause) {
- setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+ public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) {
+ final UiState state = getUiState(displayId);
+ setSystemUiVisibility(displayId, vis, 0, 0, mask,
+ state.mFullscreenStackBounds, state.mDockedStackBounds, cause);
}
- private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
- Rect fullscreenBounds, Rect dockedBounds, String cause) {
+ private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) {
// also allows calls from window manager which is in this process.
enforceStatusBarService();
if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
- updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+ updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask,
fullscreenBounds, dockedBounds);
disableLocked(
+ displayId,
mCurrentUserId,
vis & StatusBarManager.DISABLE_MASK,
mSysUiVisToken,
@@ -881,30 +875,107 @@
}
}
- private void updateUiVisibilityLocked(final int vis,
+ private void updateUiVisibilityLocked(final int displayId, final int vis,
final int fullscreenStackVis, final int dockedStackVis, final int mask,
final Rect fullscreenBounds, final Rect dockedBounds) {
- if (mSystemUiVisibility != vis
- || mFullscreenStackSysUiVisibility != fullscreenStackVis
- || mDockedStackSysUiVisibility != dockedStackVis
- || !mFullscreenStackBounds.equals(fullscreenBounds)
- || !mDockedStackBounds.equals(dockedBounds)) {
+ final UiState state = getUiState(displayId);
+ if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis,
+ fullscreenBounds, dockedBounds)) {
+ state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds,
+ dockedBounds);
+ mHandler.post(() -> {
+ if (mBar != null) {
+ try {
+ mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
+ dockedStackVis, mask, fullscreenBounds, dockedBounds);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Can not get StatusBar!");
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * @return {@link UiState} specified by {@code displayId}.
+ *
+ * <p>
+ * Note: If {@link UiState} specified by {@code displayId} does not exist, {@link UiState}
+ * will be allocated and {@code mDisplayUiState} will be updated accordingly.
+ * <p/>
+ */
+ private UiState getUiState(int displayId) {
+ UiState state = mDisplayUiState.get(displayId);
+ if (state == null) {
+ state = new UiState();
+ mDisplayUiState.put(displayId, state);
+ }
+ return state;
+ }
+
+ private class UiState {
+ private int mSystemUiVisibility = 0;
+ private int mFullscreenStackSysUiVisibility = 0;
+ private int mDockedStackSysUiVisibility = 0;
+ private final Rect mFullscreenStackBounds = new Rect();
+ private final Rect mDockedStackBounds = new Rect();
+ private boolean mMenuVisible = false;
+ private int mDisabled1 = 0;
+ private int mDisabled2 = 0;
+ private int mImeWindowVis = 0;
+ private int mImeBackDisposition = 0;
+ private boolean mShowImeSwitcher = false;
+ private IBinder mImeToken = null;
+
+ private int getDisabled1() {
+ return mDisabled1;
+ }
+
+ private int getDisabled2() {
+ return mDisabled2;
+ }
+
+ private void setDisabled(int disabled1, int disabled2) {
+ mDisabled1 = disabled1;
+ mDisabled2 = disabled2;
+ }
+
+ private boolean isMenuVisible() {
+ return mMenuVisible;
+ }
+
+ private void setMenuVisible(boolean menuVisible) {
+ mMenuVisible = menuVisible;
+ }
+
+ private boolean disableEquals(int disabled1, int disabled2) {
+ return mDisabled1 == disabled1 && mDisabled2 == disabled2;
+ }
+
+ private void setSystemUiState(final int vis, final int fullscreenStackVis,
+ final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
mSystemUiVisibility = vis;
mFullscreenStackSysUiVisibility = fullscreenStackVis;
mDockedStackSysUiVisibility = dockedStackVis;
mFullscreenStackBounds.set(fullscreenBounds);
mDockedStackBounds.set(dockedBounds);
- mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
- mask, fullscreenBounds, dockedBounds);
- } catch (RemoteException ex) {
- }
- }
- }
- });
+ }
+
+ private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis,
+ final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+ return mSystemUiVisibility == vis
+ && mFullscreenStackSysUiVisibility == fullscreenStackVis
+ && mDockedStackSysUiVisibility == dockedStackVis
+ && mFullscreenStackBounds.equals(fullscreenBounds)
+ && mDockedStackBounds.equals(dockedBounds);
+ }
+
+ private void setImeWindowState(final int vis, final int backDisposition,
+ final boolean showImeSwitcher, final IBinder token) {
+ mImeWindowVis = vis;
+ mImeBackDisposition = backDisposition;
+ mShowImeSwitcher = showImeSwitcher;
+ mImeToken = token;
}
}
@@ -939,6 +1010,7 @@
// ================================================================================
// Callbacks from the status bar service.
// ================================================================================
+ // TODO(b/118592525): refactor it as an IStatusBar API.
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
@@ -956,18 +1028,21 @@
}
}
synchronized (mLock) {
+ // TODO(b/118592525): Currently, status bar only works on the default display.
+ // Make it aware of multi-display if needed.
+ final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
- switches[1] = mSystemUiVisibility;
- switches[2] = mMenuVisible ? 1 : 0;
- switches[3] = mImeWindowVis;
- switches[4] = mImeBackDisposition;
- switches[5] = mShowImeSwitcher ? 1 : 0;
+ switches[1] = state.mSystemUiVisibility;
+ switches[2] = state.mMenuVisible ? 1 : 0;
+ switches[3] = state.mImeWindowVis;
+ switches[4] = state.mImeBackDisposition;
+ switches[5] = state.mShowImeSwitcher ? 1 : 0;
switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
- switches[7] = mFullscreenStackSysUiVisibility;
- switches[8] = mDockedStackSysUiVisibility;
- binders.add(mImeToken);
- fullscreenStackBounds.set(mFullscreenStackBounds);
- dockedStackBounds.set(mDockedStackBounds);
+ switches[7] = state.mFullscreenStackSysUiVisibility;
+ switches[8] = state.mDockedStackSysUiVisibility;
+ binders.add(state.mImeToken);
+ fullscreenStackBounds.set(state.mFullscreenStackBounds);
+ dockedStackBounds.set(state.mDockedStackBounds);
}
}
@@ -1309,8 +1384,13 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
synchronized (mLock) {
- pw.println(" mDisabled1=0x" + Integer.toHexString(mDisabled1));
- pw.println(" mDisabled2=0x" + Integer.toHexString(mDisabled2));
+ for (int i = 0; i < mDisplayUiState.size(); i++) {
+ final int key = mDisplayUiState.keyAt(i);
+ final UiState state = mDisplayUiState.get(key);
+ pw.println(" displayId=" + key);
+ pw.println(" mDisabled1=0x" + Integer.toHexString(state.getDisabled1()));
+ pw.println(" mDisabled2=0x" + Integer.toHexString(state.getDisabled2()));
+ }
final int N = mDisableRecords.size();
pw.println(" mDisableRecords.size=" + N);
for (int i=0; i<N; i++) {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index c8a68b4..6b0419e 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -136,6 +135,7 @@
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -158,6 +158,7 @@
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -180,6 +181,7 @@
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -199,7 +201,7 @@
public void onSelectionEvent(
TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException {
Preconditions.checkNotNull(event);
- validateInput(event.getPackageName(), mContext);
+ validateInput(mContext, event.getPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -220,6 +222,7 @@
ITextLanguageCallback callback) throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -242,6 +245,7 @@
IConversationActionsCallback callback) throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -263,7 +267,7 @@
throws RemoteException {
Preconditions.checkNotNull(sessionId);
Preconditions.checkNotNull(classificationContext);
- validateInput(classificationContext.getPackageName(), mContext);
+ validateInput(mContext, classificationContext.getPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -398,15 +402,17 @@
e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
}
- private static void validateInput(String packageName, Context context)
+ private static void validateInput(Context context, @Nullable String packageName)
throws RemoteException {
+ if (packageName == null) return;
+
try {
final int uid = context.getPackageManager()
.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
Preconditions.checkArgument(Binder.getCallingUid() == uid);
- } catch (IllegalArgumentException | NullPointerException |
- PackageManager.NameNotFoundException e) {
- throw new RemoteException(e.getMessage());
+ } catch (Exception e) {
+ throw new RemoteException(
+ String.format("Invalid package: name=%s, error=%s", packageName, e));
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index cfec8ef..64ff9cf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2822,8 +2822,6 @@
IoUtils.closeQuietly(stream);
if (!success) {
- wpdData.mWidth = -1;
- wpdData.mHeight = -1;
wallpaper.cropHint.set(0, 0, 0, 0);
wpdData.mPadding.set(0, 0, 0, 0);
wallpaper.name = "";
@@ -2839,6 +2837,7 @@
}
}
+ ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 7bf2c94..10542d5 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -48,12 +48,16 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.RootActivityContainer.FindTaskResult;
import static com.android.server.wm.RootActivityContainer.TAG_STATES;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.os.IBinder;
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
@@ -86,6 +90,8 @@
private ActivityTaskManagerService mService;
private RootActivityContainer mRootActivityContainer;
+ // TODO: Remove once unification is complete.
+ DisplayContent mDisplayContent;
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
@@ -138,8 +144,6 @@
// Used in updating the display size
private Point mTmpDisplaySize = new Point();
- private DisplayWindowController mWindowContainerController;
-
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
ActivityDisplay(RootActivityContainer root, Display display) {
@@ -147,19 +151,15 @@
mService = root.mService;
mDisplayId = display.getDisplayId();
mDisplay = display;
- mWindowContainerController = createWindowContainerController();
+ mDisplayContent = createDisplayContent();
updateBounds();
}
- protected DisplayWindowController createWindowContainerController() {
- return new DisplayWindowController(mDisplay, this);
+ protected DisplayContent createDisplayContent() {
+ return mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
}
- DisplayWindowController getWindowContainerController() {
- return mWindowContainerController;
- }
-
- void updateBounds() {
+ private void updateBounds() {
mDisplay.getRealSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
}
@@ -178,12 +178,15 @@
}
updateBounds();
- mWindowContainerController.onDisplayChanged();
+ if (mDisplayContent != null) {
+ mDisplayContent.updateDisplayInfo();
+ mService.mWindowManager.requestTraversal();
+ }
}
@Override
public void onInitializeOverrideConfiguration(Configuration config) {
- getOverrideConfiguration().updateFrom(config);
+ getRequestedOverrideConfiguration().updateFrom(config);
}
void addChild(ActivityStack stack, int position) {
@@ -270,9 +273,9 @@
// ActivityStack#getWindowContainerController() can be null. In this special case,
// since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
// we don't have to call WindowContainerController#positionChildAt() here.
- if (stack.getWindowContainerController() != null) {
- mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
- insertPosition, includingParents);
+ if (stack.getWindowContainerController() != null && mDisplayContent != null) {
+ mDisplayContent.positionStackAt(insertPosition,
+ stack.getWindowContainerController().mContainer, includingParents);
}
if (!wasContained) {
stack.setParent(this);
@@ -953,21 +956,28 @@
}
@Override
- public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
- final int currRotation = getOverrideConfiguration().windowConfiguration.getRotation();
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ final int currRotation =
+ getRequestedOverrideConfiguration().windowConfiguration.getRotation();
if (currRotation != ROTATION_UNDEFINED
&& currRotation != overrideConfiguration.windowConfiguration.getRotation()
- && getWindowContainerController() != null) {
- getWindowContainerController().applyRotation(currRotation,
+ && mDisplayContent != null) {
+ mDisplayContent.applyRotationLocked(currRotation,
overrideConfiguration.windowConfiguration.getRotation());
}
- super.onOverrideConfigurationChanged(overrideConfiguration);
+ super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
+ if (mDisplayContent != null) {
+ mService.mWindowManager.setNewDisplayOverrideConfiguration(
+ overrideConfiguration, mDisplayContent);
+ }
}
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
// update resources before cascade so that docked/pinned stacks use the correct info
- getWindowContainerController().preOnConfigurationChanged();
+ if (mDisplayContent != null) {
+ mDisplayContent.preOnConfigurationChanged();
+ }
super.onConfigurationChanged(newParentConfig);
}
@@ -1098,8 +1108,8 @@
private void releaseSelfIfNeeded() {
if (mStacks.isEmpty() && mRemoved) {
- mWindowContainerController.removeContainer();
- mWindowContainerController = null;
+ mDisplayContent.removeIfPossible();
+ mDisplayContent = null;
mRootActivityContainer.removeChild(this);
mRootActivityContainer.mStackSupervisor
.getKeyguardController().onDisplayRemoved(mDisplayId);
@@ -1121,7 +1131,7 @@
* @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
*/
boolean supportsSystemDecorations() {
- return mWindowContainerController.supportsSystemDecorations();
+ return mDisplayContent.supportsSystemDecorations();
}
@VisibleForTesting
@@ -1135,7 +1145,30 @@
}
void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
- mWindowContainerController.setFocusedApp(r.appToken, moveFocusNow);
+ if (mDisplayContent == null) {
+ return;
+ }
+ final AppWindowToken newFocus;
+ final IBinder token = r.appToken;
+ if (token == null) {
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
+ + mDisplayId);
+ newFocus = null;
+ } else {
+ newFocus = mService.mWindowManager.mRoot.getAppWindowToken(token);
+ if (newFocus == null) {
+ Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
+ + ", displayId=" + mDisplayId);
+ }
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
+ + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
+ }
+
+ final boolean changed = mDisplayContent.setFocusedApp(newFocus);
+ if (moveFocusNow && changed) {
+ mService.mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ true /*updateInputWindows*/);
+ }
}
/**
@@ -1283,17 +1316,21 @@
}
/**
- * See {@link DisplayWindowController#deferUpdateImeTarget()}
+ * See {@link DisplayContent#deferUpdateImeTarget()}
*/
public void deferUpdateImeTarget() {
- mWindowContainerController.deferUpdateImeTarget();
+ if (mDisplayContent != null) {
+ mDisplayContent.deferUpdateImeTarget();
+ }
}
/**
- * See {@link DisplayWindowController#deferUpdateImeTarget()}
+ * See {@link DisplayContent#deferUpdateImeTarget()}
*/
public void continueUpdateImeTarget() {
- mWindowContainerController.continueUpdateImeTarget();
+ if (mDisplayContent != null) {
+ mDisplayContent.continueUpdateImeTarget();
+ }
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 25399ef..608e450 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -96,6 +96,7 @@
import static com.android.server.am.ActivityRecordProto.VISIBLE;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
@@ -122,8 +123,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService
- .RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -157,6 +157,7 @@
import android.app.servertransaction.PipModeChangeItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.WindowVisibilityItem;
+import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -441,8 +442,13 @@
mLastReportedConfiguration.dump(pw, prefix + " ");
pw.print(prefix); pw.print("CurrentConfiguration="); pw.println(getConfiguration());
- if (!getOverrideConfiguration().equals(EMPTY)) {
- pw.println(prefix + "OverrideConfiguration=" + getOverrideConfiguration());
+ if (!getRequestedOverrideConfiguration().equals(EMPTY)) {
+ pw.println(prefix + "RequestedOverrideConfiguration="
+ + getRequestedOverrideConfiguration());
+ }
+ if (!getResolvedOverrideConfiguration().equals(getRequestedOverrideConfiguration())) {
+ pw.println(prefix + "ResolvedOverrideConfiguration="
+ + getResolvedOverrideConfiguration());
}
if (!matchParentBounds()) {
pw.println(prefix + "bounds=" + getBounds());
@@ -711,8 +717,12 @@
// to forcing the update of the picture-in-picture mode as a part of the PiP animation.
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
- final Configuration newConfig = task.computeNewOverrideConfigurationForBounds(
- targetStackBounds, null);
+ final Configuration newConfig = new Configuration();
+ if (targetStackBounds != null && !targetStackBounds.isEmpty()) {
+ task.computeResolvedOverrideConfiguration(newConfig,
+ task.getParent().getConfiguration(),
+ task.getRequestedOverrideConfiguration());
+ }
schedulePictureInPictureModeChanged(newConfig);
scheduleMultiWindowModeChanged(newConfig);
}
@@ -1030,8 +1040,6 @@
inHistory = true;
- final TaskWindowContainerController taskController = task.getWindowContainerController();
-
// TODO(b/36505427): Maybe this call should be moved inside updateOverrideConfiguration()
task.updateOverrideConfigurationFromLaunchBounds();
// Make sure override configuration is up-to-date before using to create window controller.
@@ -1043,10 +1051,9 @@
// TODO: Should this throw an exception instead?
Slog.w(TAG, "Attempted to add existing app token: " + appToken);
} else {
- final Task container = taskController.mContainer;
+ final Task container = task.getTask();
if (container == null) {
- throw new IllegalArgumentException("AppWindowContainerController: invalid "
- + " controller=" + taskController);
+ throw new IllegalArgumentException("createAppWindowToken: invalid task =" + task);
}
mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,
task.voiceSession != null, container.getDisplayContent(),
@@ -1057,7 +1064,7 @@
mLaunchTaskBehind, isAlwaysFocusable());
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
Slog.v(TAG, "addAppToken: "
- + mAppWindowToken + " controller=" + taskController + " at "
+ + mAppWindowToken + " task=" + container + " at "
+ Integer.MAX_VALUE);
}
container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
@@ -1086,6 +1093,12 @@
Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
return false;
}
+ if (mAppWindowToken.getTask() == null) {
+ // Can be removed after unification of Task and TaskRecord.
+ Slog.w(TAG_WM, "Attempted to start a window to an app token not having attached to any"
+ + " task: " + appToken);
+ return false;
+ }
return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
processRunning, allowTaskSnapshot, activityCreated, fromRecents);
@@ -1143,7 +1156,7 @@
+ " r=" + this + " (" + prevTask.getStackId() + ")");
}
- mAppWindowToken.reparent(newTask.getWindowContainerController(), position);
+ mAppWindowToken.reparent(newTask.getTask(), position);
// Reparenting prevents informing the parent stack of activity removal in the case that
// the new stack has the same parent. we must manually signal here if this is not the case.
@@ -1640,7 +1653,6 @@
final IAppTransitionAnimationSpecsFuture specsFuture =
pendingOptions.getSpecsFuture();
if (specsFuture != null) {
- // TODO(multidisplay): Shouldn't be really used anymore from next CL.
displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
specsFuture, pendingOptions.getOnAnimationStartListener(),
animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
@@ -1669,7 +1681,6 @@
.overridePendingAppTransitionStartCrossProfileApps();
break;
case ANIM_REMOTE_ANIMATION:
- // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
displayContent.mAppTransition.overridePendingAppTransitionRemote(
pendingOptions.getRemoteAnimationAdapter());
break;
@@ -1798,6 +1809,18 @@
}
mAppWindowToken.detachChildren();
}
+
+ if (state == RESUMED) {
+ mAtmService.updateBatteryStats(this, true);
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED);
+ } else if (state == PAUSED) {
+ mAtmService.updateBatteryStats(this, false);
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED);
+ } else if (state == STOPPED) {
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
+ } else if (state == DESTROYED) {
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED);
+ }
}
ActivityState getState() {
@@ -2533,21 +2556,19 @@
mTmpConfig.unset();
computeBounds(mTmpBounds);
- if (mTmpBounds.equals(getOverrideBounds())) {
+ if (mTmpBounds.equals(getRequestedOverrideBounds())) {
return;
}
setBounds(mTmpBounds);
- final Rect updatedBounds = getOverrideBounds();
-
// Bounds changed...update configuration to match.
if (!matchParentBounds()) {
- task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
- false /* overrideWidth */, false /* overrideHeight */);
+ task.computeResolvedOverrideConfiguration(mTmpConfig,
+ task.getParent().getConfiguration(), getRequestedOverrideConfiguration());
}
- onOverrideConfigurationChanged(mTmpConfig);
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
}
/** Returns true if the configuration is compatible with this activity. */
@@ -2604,11 +2625,11 @@
if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
// The display matches or is less than the activity aspect ratio, so nothing else to do.
// Return the existing bounds. If this method is running for the first time,
- // {@link #getOverrideBounds()} will be empty (representing no override). If the method has run
- // before, then effect of {@link #getOverrideBounds()} will already have been applied to the
- // value returned from {@link getConfiguration}. Refer to
- // {@link TaskRecord#computeOverrideConfiguration}.
- outBounds.set(getOverrideBounds());
+ // {@link #getRequestedOverrideBounds()} will be empty (representing no override). If
+ // the method has run before, then effect of {@link #getRequestedOverrideBounds()} will
+ // already have been applied to the value returned from {@link getConfiguration}. Refer
+ // to {@link TaskRecord#computeOverrideConfiguration}.
+ outBounds.set(getRequestedOverrideBounds());
return;
}
@@ -2771,7 +2792,7 @@
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
if (hasResizeChange) {
final boolean isDragResizing =
- getTaskRecord().getWindowContainerController().isDragResizing();
+ getTaskRecord().getTask().isDragResizing();
mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
: RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
} else {
@@ -2939,7 +2960,7 @@
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(
- getDisplay().getWindowContainerController().isNextTransitionForward());
+ getDisplay().mDisplayContent.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index d40a4fa..2663d99 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -264,8 +264,8 @@
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
getStackDockedModeBounds(null, null, mTmpRect2, mTmpRect3);
- mStackSupervisor.resizeDockedStackLocked(
- getOverrideBounds(), mTmpRect2, mTmpRect2, null, null, PRESERVE_WINDOWS);
+ mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect2,
+ mTmpRect2, null, null, PRESERVE_WINDOWS);
}
mRootActivityContainer.updateUIDsPresentOnDisplay();
}
@@ -294,13 +294,6 @@
// stack and the new stack will be on top of all stacks.
static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
- // The height/width divide used when fitting a task within a bounds with method
- // {@link #fitWithinBounds}.
- // We always want the task to to be visible in the bounds without affecting its size when
- // fitting. To make sure this is the case, we don't adjust the task left or top side pass
- // the input bounds right or bottom side minus the width or height divided by this value.
- private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
-
final ActivityTaskManagerService mService;
private final WindowManagerService mWindowManager;
T mWindowContainerController;
@@ -365,9 +358,9 @@
private boolean mUpdateBoundsDeferred;
private boolean mUpdateBoundsDeferredCalled;
+ private boolean mUpdateDisplayedBoundsDeferredCalled;
private final Rect mDeferredBounds = new Rect();
- private final Rect mDeferredTaskBounds = new Rect();
- private final Rect mDeferredTaskInsetBounds = new Rect();
+ private final Rect mDeferredDisplayedBounds = new Rect();
int mCurrentUser;
@@ -562,7 +555,7 @@
// Update bounds if applicable
boolean hasNewOverrideBounds = false;
// Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (getOverrideWindowingMode() == WINDOWING_MODE_PINNED) {
+ if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_PINNED) {
// Pinned calculation already includes rotation
mTmpRect2.set(mTmpRect);
hasNewOverrideBounds = getWindowContainerController().mContainer
@@ -574,7 +567,7 @@
// bounds were on the pre-rotated display.
if (prevRotation != newRotation) {
mTmpRect2.set(mTmpRect);
- getDisplay().getWindowContainerController().mContainer
+ getDisplay().mDisplayContent
.rotateBounds(newParentConfig.windowConfiguration.getBounds(),
prevRotation, newRotation, mTmpRect2);
hasNewOverrideBounds = true;
@@ -589,8 +582,8 @@
|| prevScreenH != getConfiguration().screenHeightDp) {
// Use override windowing mode to prevent extra bounds changes if inheriting
// the mode.
- if (getOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || getOverrideWindowingMode()
+ if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || getRequestedOverrideWindowingMode()
== WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
mTmpRect2.set(mTmpRect);
getWindowContainerController().mContainer
@@ -602,14 +595,17 @@
}
if (getWindowingMode() != prevWindowingMode) {
// Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (getOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
getStackDockedModeBounds(null, null, mTmpRect2, mTmpRect3);
// immediately resize so docked bounds are available in onSplitScreenModeActivated
- resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
- } else if (getOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ setTaskDisplayedBounds(null);
+ setTaskBounds(mTmpRect2);
+ setBounds(mTmpRect2);
+ } else if (
+ getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
Rect dockedBounds = display.getSplitScreenPrimaryStack().getBounds();
- final boolean isMinimizedDock = getDisplay().getWindowContainerController()
- .mContainer.getDockedDividerController().isMinimizedDock();
+ final boolean isMinimizedDock =
+ getDisplay().mDisplayContent.getDockedDividerController().isMinimizedDock();
if (isMinimizedDock) {
TaskRecord topTask = display.getSplitScreenPrimaryStack().topTask();
if (topTask != null) {
@@ -672,7 +668,7 @@
void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
boolean enteringSplitScreenMode, boolean deferEnsuringVisibility, boolean creating) {
final int currentMode = getWindowingMode();
- final int currentOverrideMode = getOverrideWindowingMode();
+ final int currentOverrideMode = getRequestedOverrideWindowingMode();
final ActivityDisplay display = getDisplay();
final TaskRecord topTask = topTask();
final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
@@ -725,7 +721,7 @@
// You are already in the window mode, so we can skip most of the work below. However,
// it's possible that we have inherited the current windowing mode from a parent. So,
// fulfill this method's contract by setting the override mode directly.
- getOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
return;
}
@@ -791,7 +787,7 @@
mWindowContainerController.getRawBounds(mTmpRect2);
}
- if (!Objects.equals(getOverrideBounds(), mTmpRect2)) {
+ if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
}
} finally {
@@ -910,7 +906,7 @@
}
void positionChildWindowContainerAtTop(TaskRecord child) {
- mWindowContainerController.positionChildAtTop(child.getWindowContainerController(),
+ mWindowContainerController.positionChildAtTop(child.getTask(),
true /* includingParents */);
}
@@ -920,7 +916,7 @@
// task to bottom, the next focusable stack on the same display should be focused.
final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
child.getStack(), true /* ignoreCurrent */);
- mWindowContainerController.positionChildAtBottom(child.getWindowContainerController(),
+ mWindowContainerController.positionChildAtBottom(child.getTask(),
nextFocusableStack == null /* includingParents */);
}
@@ -948,17 +944,19 @@
* be resized to that bounds.
*/
void continueUpdateBounds() {
- final boolean wasDeferred = mUpdateBoundsDeferred;
- mUpdateBoundsDeferred = false;
- if (wasDeferred && mUpdateBoundsDeferredCalled) {
- resize(mDeferredBounds.isEmpty() ? null : mDeferredBounds,
- mDeferredTaskBounds.isEmpty() ? null : mDeferredTaskBounds,
- mDeferredTaskInsetBounds.isEmpty() ? null : mDeferredTaskInsetBounds);
+ if (mUpdateBoundsDeferred) {
+ mUpdateBoundsDeferred = false;
+ if (mUpdateBoundsDeferredCalled) {
+ setTaskBounds(mDeferredBounds);
+ setBounds(mDeferredBounds);
+ }
+ if (mUpdateDisplayedBoundsDeferredCalled) {
+ setTaskDisplayedBounds(mDeferredDisplayedBounds);
+ }
}
}
- boolean updateBoundsAllowed(Rect bounds, Rect tempTaskBounds,
- Rect tempTaskInsetBounds) {
+ boolean updateBoundsAllowed(Rect bounds) {
if (!mUpdateBoundsDeferred) {
return true;
}
@@ -967,20 +965,23 @@
} else {
mDeferredBounds.setEmpty();
}
- if (tempTaskBounds != null) {
- mDeferredTaskBounds.set(tempTaskBounds);
- } else {
- mDeferredTaskBounds.setEmpty();
- }
- if (tempTaskInsetBounds != null) {
- mDeferredTaskInsetBounds.set(tempTaskInsetBounds);
- } else {
- mDeferredTaskInsetBounds.setEmpty();
- }
mUpdateBoundsDeferredCalled = true;
return false;
}
+ boolean updateDisplayedBoundsAllowed(Rect bounds) {
+ if (!mUpdateBoundsDeferred) {
+ return true;
+ }
+ if (bounds != null) {
+ mDeferredDisplayedBounds.set(bounds);
+ } else {
+ mDeferredDisplayedBounds.setEmpty();
+ }
+ mUpdateDisplayedBoundsDeferredCalled = true;
+ return false;
+ }
+
@Override
public int setBounds(Rect bounds) {
return super.setBounds(!inMultiWindowMode() ? null : bounds);
@@ -1616,7 +1617,6 @@
try {
EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
- mService.updateUsageStats(prev, false);
mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
@@ -1796,7 +1796,7 @@
// focus). Also if there is an active pinned stack - we always want to notify it about
// task stack changes, because its positioning may depend on it.
if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
- || getDisplay().hasPinnedStack()) {
+ || (getDisplay() != null && getDisplay().hasPinnedStack())) {
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
@@ -2713,16 +2713,16 @@
// that the previous one will be hidden soon. This way it can know
// to ignore it when computing the desired screen orientation.
boolean anim = true;
- final DisplayWindowController dwc = getDisplay().getWindowContainerController();
+ final DisplayContent dc = getDisplay().mDisplayContent;
if (prev != null) {
if (prev.finishing) {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare close transition: prev=" + prev);
if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
anim = false;
- dwc.prepareAppTransition(TRANSIT_NONE, false);
+ dc.prepareAppTransition(TRANSIT_NONE, false);
} else {
- dwc.prepareAppTransition(
+ dc.prepareAppTransition(
prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_CLOSE
: TRANSIT_TASK_CLOSE, false);
}
@@ -2732,9 +2732,9 @@
"Prepare open transition: prev=" + prev);
if (mStackSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dwc.prepareAppTransition(TRANSIT_NONE, false);
+ dc.prepareAppTransition(TRANSIT_NONE, false);
} else {
- dwc.prepareAppTransition(
+ dc.prepareAppTransition(
prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_OPEN
: next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
: TRANSIT_TASK_OPEN, false);
@@ -2744,9 +2744,9 @@
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
if (mStackSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dwc.prepareAppTransition(TRANSIT_NONE, false);
+ dc.prepareAppTransition(TRANSIT_NONE, false);
} else {
- dwc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
+ dc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
}
}
@@ -2868,8 +2868,7 @@
next.clearOptionsLocked();
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.getReportedProcState(),
- getDisplay().getWindowContainerController()
- .isNextTransitionForward()));
+ getDisplay().mDisplayContent.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
@@ -2984,7 +2983,7 @@
position = getAdjustedPositionForTask(task, position, null /* starting */);
mTaskHistory.remove(task);
mTaskHistory.add(position, task);
- mWindowContainerController.positionChildAt(task.getWindowContainerController(), position);
+ mWindowContainerController.positionChildAt(task.getTask(), position);
updateTaskMovement(task, true);
}
@@ -3071,11 +3070,11 @@
task.setFrontOfTask();
if (!isHomeOrRecentsStack() || numActivities() > 0) {
- final DisplayWindowController dwc = getDisplay().getWindowContainerController();
+ final DisplayContent dc = getDisplay().mDisplayContent;
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare open transition: starting " + r);
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- dwc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
+ dc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
mStackSupervisor.mNoAnimActivities.add(r);
} else {
int transit = TRANSIT_ACTIVITY_OPEN;
@@ -3094,7 +3093,7 @@
transit = TRANSIT_TASK_OPEN;
}
}
- dwc.prepareAppTransition(transit, keepCurTransition);
+ dc.prepareAppTransition(transit, keepCurTransition);
mStackSupervisor.mNoAnimActivities.remove(r);
}
boolean doShow = true;
@@ -3723,7 +3722,7 @@
int taskNdx = mTaskHistory.indexOf(finishedTask);
final TaskRecord task = finishedTask;
int activityNdx = task.mActivities.indexOf(r);
- getDisplay().getWindowContainerController().prepareAppTransition(
+ getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
finishedTask = task;
@@ -3889,7 +3888,7 @@
mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
task.taskId);
}
- getDisplay().getWindowContainerController().prepareAppTransition(transit, false);
+ getDisplay().mDisplayContent.prepareAppTransition(transit, false);
// Tell window manager to prepare for this one to be removed.
r.setVisibility(false);
@@ -3944,10 +3943,10 @@
}
private void prepareActivityHideTransitionAnimation(ActivityRecord r, int transit) {
- final DisplayWindowController dwc = getDisplay().getWindowContainerController();
- dwc.prepareAppTransition(transit, false);
+ final DisplayContent dc = getDisplay().mDisplayContent;
+ dc.prepareAppTransition(transit, false);
r.setVisibility(false);
- dwc.executeAppTransition();
+ dc.executeAppTransition();
if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
}
@@ -4649,9 +4648,6 @@
r.mUserId, System.identityHashCode(r),
r.getTaskRecord().taskId, r.shortComponentName,
"proc died without state saved");
- if (r.getState() == RESUMED) {
- mService.updateUsageStats(r, false);
- }
}
} else {
// We have the current state for this activity, so
@@ -4691,7 +4687,7 @@
ActivityOptions.abort(options);
}
}
- getDisplay().getWindowContainerController().prepareAppTransition(transit, false);
+ getDisplay().mDisplayContent.prepareAppTransition(transit, false);
}
private void updateTaskMovement(TaskRecord task, boolean toFront) {
@@ -4760,8 +4756,7 @@
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
if (noAnimation) {
- getDisplay().getWindowContainerController().prepareAppTransition(
- TRANSIT_NONE, false);
+ getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_NONE, false);
if (r != null) {
mStackSupervisor.mNoAnimActivities.add(r);
}
@@ -4843,8 +4838,7 @@
mTaskHistory.add(0, tr);
updateTaskMovement(tr, false);
- getDisplay().getWindowContainerController().prepareAppTransition(
- TRANSIT_TASK_TO_BACK, false);
+ getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
moveToBack("moveTaskToBackLocked", tr);
if (inPinnedWindowingMode()) {
@@ -4914,7 +4908,7 @@
// TODO: Can only be called from special methods in ActivityStackSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
- if (!updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
+ if (!updateBoundsAllowed(bounds)) {
return;
}
@@ -4928,29 +4922,18 @@
for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
- if (inFreeformWindowingMode()) {
- // TODO(b/71028874): Can be removed since each freeform task is its own
- // stack.
- // For freeform stack we don't adjust the size of the tasks to match that
- // of the stack, but we do try to make sure the tasks are still contained
- // with the bounds of the stack.
- if (task.getOverrideBounds() != null) {
- mTmpRect2.set(task.getOverrideBounds());
- fitWithinBounds(mTmpRect2, bounds);
- task.updateOverrideConfiguration(mTmpRect2);
- }
- } else {
- task.updateOverrideConfiguration(taskBounds, insetBounds);
- }
+ task.updateOverrideConfiguration(taskBounds, insetBounds);
}
- mTmpBounds.put(task.taskId, task.getOverrideBounds());
- if (tempTaskInsetBounds != null) {
- mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+ if (task.hasDisplayedBounds()) {
+ mTmpBounds.put(task.taskId, task.getDisplayedBounds());
+ mTmpInsetBounds.put(task.taskId, task.getRequestedOverrideBounds());
+ } else {
+ mTmpBounds.put(task.taskId, task.getRequestedOverrideBounds());
+ mTmpInsetBounds.put(task.taskId, null);
}
}
- mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
setBounds(bounds);
}
@@ -4960,41 +4943,37 @@
/**
- * Adjust bounds to stay within stack bounds.
- *
- * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
- * that keep them unchanged, but be contained within the stack bounds.
- *
- * @param bounds Bounds to be adjusted.
- * @param stackBounds Bounds within which the other bounds should remain.
+ * Until we can break this "set task bounds to same as stack bounds" behavior, this
+ * basically resizes both stack and task bounds to the same bounds.
*/
- private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
- if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) {
+ void setTaskBounds(Rect bounds) {
+ if (!updateBoundsAllowed(bounds)) {
return;
}
- if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
- final int maxRight = stackBounds.right
- - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
- int horizontalDiff = stackBounds.left - bounds.left;
- if ((horizontalDiff < 0 && bounds.left >= maxRight)
- || (bounds.left + horizontalDiff >= maxRight)) {
- horizontalDiff = maxRight - bounds.left;
+ for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+ final TaskRecord task = mTaskHistory.get(i);
+ if (task.isResizeable()) {
+ task.setBounds(bounds);
+ } else {
+ task.setBounds(null);
}
- bounds.left += horizontalDiff;
- bounds.right += horizontalDiff;
+ }
+ }
+
+ /** Helper to setDisplayedBounds on all child tasks */
+ void setTaskDisplayedBounds(Rect bounds) {
+ if (!updateDisplayedBoundsAllowed(bounds)) {
+ return;
}
- if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
- final int maxBottom = stackBounds.bottom
- - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
- int verticalDiff = stackBounds.top - bounds.top;
- if ((verticalDiff < 0 && bounds.top >= maxBottom)
- || (bounds.top + verticalDiff >= maxBottom)) {
- verticalDiff = maxBottom - bounds.top;
+ for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+ final TaskRecord task = mTaskHistory.get(i);
+ if (bounds == null || bounds.isEmpty()) {
+ task.setDisplayedBounds(null);
+ } else if (task.isResizeable()) {
+ task.setDisplayedBounds(bounds);
}
- bounds.top += verticalDiff;
- bounds.bottom += verticalDiff;
}
}
@@ -5166,7 +5145,7 @@
+ r.intent.getComponent().flattenToShortString());
// Force the destroy to skip right to removal.
r.app = null;
- getDisplay().getWindowContainerController().prepareAppTransition(
+ getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
"handleAppCrashedLocked");
@@ -5188,7 +5167,7 @@
pw.println("");
}
pw.println(prefix + "Task id #" + task.taskId);
- pw.println(prefix + "mBounds=" + task.getOverrideBounds());
+ pw.println(prefix + "mBounds=" + task.getRequestedOverrideBounds());
pw.println(prefix + "mMinWidth=" + task.mMinWidth);
pw.println(prefix + "mMinHeight=" + task.mMinHeight);
pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
@@ -5346,9 +5325,9 @@
if (!mStackSupervisor.getLaunchParamsController()
.layoutTask(task, info.windowLayout, activity, source, options)
&& !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
- task.updateOverrideConfiguration(getOverrideBounds());
+ task.updateOverrideConfiguration(getRequestedOverrideBounds());
}
- task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+ task.createTask(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
return task;
}
@@ -5504,7 +5483,7 @@
}
void executeAppTransition(ActivityOptions options) {
- getDisplay().getWindowContainerController().executeAppTransition();
+ getDisplay().mDisplayContent.executeAppTransition();
ActivityOptions.abort(options);
}
@@ -5538,7 +5517,7 @@
}
proto.write(DISPLAY_ID, mDisplayId);
if (!matchParentBounds()) {
- final Rect bounds = getOverrideBounds();
+ final Rect bounds = getRequestedOverrideBounds();
bounds.writeToProto(proto, BOUNDS);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f3c5630..4339e51 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -818,7 +818,7 @@
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
- final DisplayWindowController dwc = r.getDisplay().getWindowContainerController();
+ final DisplayContent dc = r.getDisplay().mDisplayContent;
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
@@ -827,12 +827,12 @@
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.icicle, r.persistentState, results, newIntents,
- dwc.isNextTransitionForward(), profilerInfo));
+ dc.isNextTransitionForward(), profilerInfo));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(dwc.isNextTransitionForward());
+ lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
@@ -1868,7 +1868,7 @@
stack.addTask(task, onTop, "restoreRecentTask");
// TODO: move call for creation here and other place into Stack.addTask()
- task.createWindowContainer(onTop, true /* showForAllUsers */);
+ task.createTask(onTop, true /* showForAllUsers */);
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -2040,9 +2040,6 @@
mStoppingActivities.remove(r);
final ActivityStack stack = r.getActivityStack();
- if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
- mService.updateUsageStats(r, true);
- }
if (stack.getDisplay().allResumedActivitiesComplete()) {
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// Make sure activity & window visibility should be identical
@@ -2410,7 +2407,7 @@
return;
}
- scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getOverrideBounds());
+ scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getRequestedOverrideBounds());
}
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bc2136e..57bfc29 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1565,7 +1565,7 @@
mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
- mTargetStack.getDisplay().getWindowContainerController().executeAppTransition();
+ mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
} else {
// If the target stack was not previously focusable (previous top running activity
// on that stack was not visible) then any prior calls to move the stack to the
@@ -2506,8 +2506,9 @@
// full resolution.
mLaunchParams.mPreferredDisplayId =
mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
+ final boolean onTop = aOptions == null || !aOptions.getAvoidMoveToFront();
final ActivityStack stack =
- mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams);
+ mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams);
mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
return stack;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0cdbedb..0fc890a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -402,7 +402,8 @@
int wakefulness);
/** Writes the current window process states to the proto stream. */
- public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage);
+ public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage,
+ int wakeFullness, boolean testPssMode);
/** Dump the current activities state. */
public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0967afd..9861157 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -863,6 +863,13 @@
*/
Configuration getGlobalConfigurationForCallingPid() {
final int pid = Binder.getCallingPid();
+ return getGlobalConfigurationForPid(pid);
+ }
+
+ /**
+ * Return the global configuration used by the process corresponding to the given pid.
+ */
+ Configuration getGlobalConfigurationForPid(int pid) {
if (pid == MY_PID || pid < 0) {
return getGlobalConfiguration();
}
@@ -1731,7 +1738,7 @@
if (self.isState(
ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
- self.getDisplay().getWindowContainerController().overridePendingAppTransition(
+ self.getDisplay().mDisplayContent.mAppTransition.overridePendingAppTransition(
packageName, enterAnim, exitAnim, null);
}
@@ -3066,12 +3073,11 @@
// Get top display of front most application.
final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack != null) {
- final DisplayWindowController dwc =
- focusedStack.getDisplay().getWindowContainerController();
- dwc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
- dwc.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
+ final DisplayContent dc = focusedStack.getDisplay().mDisplayContent;
+ dc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
+ dc.mAppTransition.overrideInPlaceAppTransition(activityOptions.getPackageName(),
activityOptions.getCustomInPlaceResId());
- dwc.executeAppTransition();
+ dc.executeAppTransition();
}
}
@@ -3793,7 +3799,7 @@
// changed, so we should reflect that check here as well.
final PinnedActivityStack stack = r.getActivityStack();
final PinnedStackWindowController windowController = stack.getWindowContainerController();
- return !windowController.isAnimatingBoundsToFullscreen();
+ return !windowController.mContainer.isAnimatingBoundsToFullscreen();
}
@Override
@@ -4701,26 +4707,21 @@
}
}
- void writeSleepStateToProto(ProtoOutputStream proto) {
+ private void writeSleepStateToProto(ProtoOutputStream proto, int wakeFullness,
+ boolean testPssMode) {
+ final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
+ PowerManagerInternal.wakefulnessToProtoEnum(wakeFullness));
for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) {
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
st.toString());
}
-
- if (mRunningVoice != null) {
- final long vrToken = proto.start(
- ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
- proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
- mRunningVoice.toString());
- mVoiceWakeLock.writeToProto(
- proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
- proto.end(vrToken);
- }
-
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN,
mShuttingDown);
- mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE,
+ testPssMode);
+ proto.end(sleepToken);
}
int getCurrentUserId() {
@@ -5233,13 +5234,20 @@
mH.post(mAmInternal::updateCpuStats);
}
- void updateUsageStats(ActivityRecord component, boolean resumed) {
- final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
+ void updateBatteryStats(ActivityRecord component, boolean resumed) {
+ final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateBatteryStats,
mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId,
resumed);
mH.sendMessage(m);
}
+ void updateActivityUsageStats(ActivityRecord activity, int event) {
+ final Message m = PooledLambda.obtainMessage(
+ ActivityManagerInternal::updateActivityUsageStats, mAmInternal,
+ activity.mActivityComponent, activity.mUserId, event, activity.appToken);
+ mH.sendMessage(m);
+ }
+
void setBooting(boolean booting) {
mAmInternal.setBooting(booting);
}
@@ -5768,17 +5776,18 @@
if (activityDisplay == null) {
return;
}
- final DisplayWindowController dwc = activityDisplay.getWindowContainerController();
- final boolean wasTransitionSet = dwc.getPendingAppTransition() != TRANSIT_NONE;
+ final DisplayContent dc = activityDisplay.mDisplayContent;
+ final boolean wasTransitionSet =
+ dc.mAppTransition.getAppTransition() != TRANSIT_NONE;
if (!wasTransitionSet) {
- dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
+ dc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
}
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// If there was a transition set already we don't want to interfere with it as we
// might be starting it too early.
if (!wasTransitionSet) {
- dwc.executeAppTransition();
+ dc.executeAppTransition();
}
}
if (callback != null) {
@@ -6268,18 +6277,20 @@
finishInstrumentationCallback.run();
}
- mWindowManager.deferSurfaceLayout();
- try {
- if (!restarting && hasVisibleActivities
- && !mRootActivityContainer.resumeFocusedStacksTopActivities()) {
- // If there was nothing to resume, and we are not already restarting this
- // process, but there is a visible activity that is hosted by the process...
- // then make sure all visible activities are running, taking care of
- // restarting this process.
- mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ if (!restarting && hasVisibleActivities) {
+ mWindowManager.deferSurfaceLayout();
+ try {
+ if (!mRootActivityContainer.resumeFocusedStacksTopActivities()) {
+ // If there was nothing to resume, and we are not already restarting
+ // this process, but there is a visible activity that is hosted by the
+ // process...then make sure all visible activities are running, taking
+ // care of restarting this process.
+ mRootActivityContainer.ensureActivitiesVisible(null, 0,
+ !PRESERVE_WINDOWS);
+ }
+ } finally {
+ mWindowManager.continueSurfaceLayout();
}
- } finally {
- mWindowManager.continueSurfaceLayout();
}
}
}
@@ -6599,12 +6610,24 @@
}
@Override
- public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage) {
+ public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage,
+ int wakeFullness, boolean testPssMode) {
synchronized (mGlobalLock) {
if (dumpPackage == null) {
getGlobalConfiguration().writeToProto(proto, GLOBAL_CONFIGURATION);
proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
- writeSleepStateToProto(proto);
+ writeSleepStateToProto(proto, wakeFullness, testPssMode);
+ if (mRunningVoice != null) {
+ final long vrToken = proto.start(
+ ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
+ mRunningVoice.toString());
+ mVoiceWakeLock.writeToProto(
+ proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
+ proto.end(vrToken);
+ }
+ mVrController.writeToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
if (mController != null) {
final long token = proto.start(CONTROLLER);
proto.write(CONTROLLER, mController.toString());
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fb93d39..8624bff 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -746,6 +746,9 @@
@Override
void removeImmediately() {
onRemovedFromDisplay();
+ if (mActivityRecord != null) {
+ mActivityRecord.unregisterConfigurationChangeListener(this);
+ }
super.removeImmediately();
}
@@ -1175,21 +1178,14 @@
}
}
- void reparent(TaskWindowContainerController taskController, int position) {
+ void reparent(Task task, int position) {
if (DEBUG_ADD_REMOVE) {
Slog.i(TAG_WM, "reparent: moving app token=" + this
- + " to task=" + taskController + " at " + position);
+ + " to task=" + task.mTaskId + " at " + position);
}
- final Task task = taskController.mContainer;
if (task == null) {
- throw new IllegalArgumentException("reparent: could not find task="
- + taskController);
+ throw new IllegalArgumentException("reparent: could not find task");
}
- reparent(task, position);
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
-
- void reparent(Task task, int position) {
final Task currentTask = getTask();
if (task == currentTask) {
throw new IllegalArgumentException(
@@ -1220,6 +1216,7 @@
onDisplayChanged(displayContent);
prevDisplayContent.setLayoutNeeded();
}
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
@Override
@@ -2178,9 +2175,9 @@
final TaskStack stack = getStack();
final Task task = getTask();
if (task != null && task.inFreeformWindowingMode()) {
- task.getRelativePosition(outPosition);
+ task.getRelativeDisplayedPosition(outPosition);
} else if (stack != null) {
- stack.getRelativePosition(outPosition);
+ stack.getRelativeDisplayedPosition(outPosition);
}
// Always use stack bounds in order to have the ability to animate outside the task region.
@@ -2193,6 +2190,18 @@
outBounds.offsetTo(0, 0);
}
+ @Override
+ Rect getDisplayedBounds() {
+ final Task task = getTask();
+ if (task != null) {
+ final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
+ if (!overrideDisplayedBounds.isEmpty()) {
+ return overrideDisplayedBounds;
+ }
+ }
+ return getBounds();
+ }
+
boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
boolean isVoiceInteraction) {
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index a335fa2..5b20af3 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -219,7 +219,7 @@
}
private boolean updateStateLw(final int state) {
- if (state != mState) {
+ if (mWin != null && state != mState) {
mState = state;
if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
mHandler.post(new Runnable() {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 64553a8..ded45c9 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -49,20 +49,28 @@
*/
public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
/**
- * {@link #Rect} returned from {@link #getOverrideBounds()} to prevent original value from being
- * set directly.
+ * {@link #Rect} returned from {@link #getRequestedOverrideBounds()} to prevent original value
+ * from being set directly.
*/
private Rect mReturnBounds = new Rect();
- /** Contains override configuration settings applied to this configuration container. */
- private Configuration mOverrideConfiguration = new Configuration();
+ /**
+ * Contains requested override configuration settings applied to this configuration container.
+ */
+ private Configuration mRequestedOverrideConfiguration = new Configuration();
- /** True if mOverrideConfiguration is not empty */
+ /**
+ * Contains the requested override configuration with parent and policy constraints applied.
+ * This is the set of overrides that gets applied to the full and merged configurations.
+ */
+ private Configuration mResolvedOverrideConfiguration = new Configuration();
+
+ /** True if mRequestedOverrideConfiguration is not empty */
private boolean mHasOverrideConfiguration;
/**
* Contains full configuration applied to this configuration container. Corresponds to full
- * parent's config with applied {@link #mOverrideConfiguration}.
+ * parent's config with applied {@link #mResolvedOverrideConfiguration}.
*/
private Configuration mFullConfiguration = new Configuration();
@@ -102,41 +110,62 @@
* @see #mFullConfiguration
*/
public void onConfigurationChanged(Configuration newParentConfig) {
+ mTmpConfig.setTo(mResolvedOverrideConfiguration);
+ resolveOverrideConfiguration(newParentConfig);
mFullConfiguration.setTo(newParentConfig);
- mFullConfiguration.updateFrom(mOverrideConfiguration);
+ mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+ if (!mTmpConfig.equals(mResolvedOverrideConfiguration)) {
+ onMergedOverrideConfigurationChanged();
+ // This depends on the assumption that change-listeners don't do
+ // their own override resolution. This way, dependent hierarchies
+ // can stay properly synced-up with a primary hierarchy's constraints.
+ // Since the hierarchies will be merged, this whole thing will go away
+ // before the assumption will be broken.
+ // Inform listeners of the change.
+ for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
+ mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
+ mResolvedOverrideConfiguration);
+ }
+ }
for (int i = getChildCount() - 1; i >= 0; --i) {
final ConfigurationContainer child = getChildAt(i);
child.onConfigurationChanged(mFullConfiguration);
}
}
- /** Returns override configuration applied to this configuration container. */
- public Configuration getOverrideConfiguration() {
- return mOverrideConfiguration;
+ /**
+ * Resolves the current requested override configuration into
+ * {@link #mResolvedOverrideConfiguration}
+ *
+ * @param newParentConfig The new parent configuration to resolve overrides against.
+ */
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
+ }
+
+ /** Returns requested override configuration applied to this configuration container. */
+ public Configuration getRequestedOverrideConfiguration() {
+ return mRequestedOverrideConfiguration;
+ }
+
+ /** Returns the resolved override configuration. */
+ Configuration getResolvedOverrideConfiguration() {
+ return mResolvedOverrideConfiguration;
}
/**
* Update override configuration and recalculate full config.
- * @see #mOverrideConfiguration
+ * @see #mRequestedOverrideConfiguration
* @see #mFullConfiguration
*/
- public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
// Pre-compute this here, so we don't need to go through the entire Configuration when
// writing to proto (which has significant cost if we write a lot of empty configurations).
mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
- mOverrideConfiguration.setTo(overrideConfiguration);
+ mRequestedOverrideConfiguration.setTo(overrideConfiguration);
// Update full configuration of this container and all its children.
final ConfigurationContainer parent = getParent();
onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
- // Update merged override config of this container and all its children.
- onMergedOverrideConfigurationChanged();
-
- // Use the updated override configuration to notify listeners.
- mTmpConfig.setTo(mOverrideConfiguration);
- // Inform listeners of the change.
- for (int i = mChangeListeners.size() - 1; i >=0; --i) {
- mChangeListeners.get(i).onOverrideConfigurationChanged(mTmpConfig);
- }
}
/**
@@ -157,9 +186,9 @@
final ConfigurationContainer parent = getParent();
if (parent != null) {
mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration());
- mMergedOverrideConfiguration.updateFrom(mOverrideConfiguration);
+ mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration);
} else {
- mMergedOverrideConfiguration.setTo(mOverrideConfiguration);
+ mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
}
for (int i = getChildCount() - 1; i >= 0; --i) {
final ConfigurationContainer child = getChildAt(i);
@@ -168,24 +197,26 @@
}
/**
- * Indicates whether this container has not specified any bounds different from its parent. In
- * this case, it will inherit the bounds of the first ancestor which specifies a bounds.
- * @return {@code true} if no explicit bounds have been set at this container level.
+ * Indicates whether this container has not requested any bounds different from its parent. In
+ * this case, it will inherit the bounds of the first ancestor which specifies a bounds subject
+ * to policy constraints.
+ *
+ * @return {@code true} if no explicit bounds have been requested at this container level.
* {@code false} otherwise.
*/
public boolean matchParentBounds() {
- return getOverrideBounds().isEmpty();
+ return getRequestedOverrideBounds().isEmpty();
}
/**
- * Returns whether the bounds specified is considered the same as the existing override bounds.
- * This is either when the two bounds are equal or the override bounds is empty and the
- * specified bounds is null.
+ * Returns whether the bounds specified are considered the same as the existing requested
+ * override bounds. This is either when the two bounds are equal or the requested override
+ * bounds are empty and the specified bounds is null.
*
* @return {@code true} if the bounds are equivalent, {@code false} otherwise
*/
- public boolean equivalentOverrideBounds(Rect bounds) {
- return equivalentBounds(getOverrideBounds(), bounds);
+ public boolean equivalentRequestedOverrideBounds(Rect bounds) {
+ return equivalentBounds(getRequestedOverrideBounds(), bounds);
}
/**
@@ -212,50 +243,52 @@
}
/**
- * Returns the current bounds explicitly set on this container. The {@link Rect} handed back is
+ * Returns the bounds requested on this container. These may not be the actual bounds the
+ * container ends up with due to policy constraints. The {@link Rect} handed back is
* shared for all calls to this method and should not be modified.
*/
- public Rect getOverrideBounds() {
- mReturnBounds.set(getOverrideConfiguration().windowConfiguration.getBounds());
+ public Rect getRequestedOverrideBounds() {
+ mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getBounds());
return mReturnBounds;
}
/**
- * Returns {@code true} if the {@link WindowConfiguration} in the override
+ * Returns {@code true} if the {@link WindowConfiguration} in the requested override
* {@link Configuration} specifies bounds.
*/
public boolean hasOverrideBounds() {
- return !getOverrideBounds().isEmpty();
+ return !getRequestedOverrideBounds().isEmpty();
}
/**
* Sets the passed in {@link Rect} to the current bounds.
- * @see {@link #getOverrideBounds()}.
+ * @see {@link #getRequestedOverrideBounds()}.
*/
- public void getOverrideBounds(Rect outBounds) {
- outBounds.set(getOverrideBounds());
+ public void getRequestedOverrideBounds(Rect outBounds) {
+ outBounds.set(getRequestedOverrideBounds());
}
/**
* Sets the bounds at the current hierarchy level, overriding any bounds set on an ancestor.
- * This value will be reported when {@link #getBounds()} and {@link #getOverrideBounds()}. If
+ * This value will be reported when {@link #getBounds()} and
+ * {@link #getRequestedOverrideBounds()}. If
* an empty {@link Rect} or null is specified, this container will be considered to match its
* parent bounds {@see #matchParentBounds} and will inherit bounds from its parent.
* @param bounds The bounds defining the container size.
* @return a bitmask representing the types of changes made to the bounds.
*/
public int setBounds(Rect bounds) {
- int boundsChange = diffOverrideBounds(bounds);
+ int boundsChange = diffRequestedOverrideBounds(bounds);
if (boundsChange == BOUNDS_CHANGE_NONE) {
return boundsChange;
}
- mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
mTmpConfig.windowConfiguration.setBounds(bounds);
- onOverrideConfigurationChanged(mTmpConfig);
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
return boundsChange;
}
@@ -265,14 +298,14 @@
return setBounds(mTmpRect);
}
- int diffOverrideBounds(Rect bounds) {
- if (equivalentOverrideBounds(bounds)) {
+ int diffRequestedOverrideBounds(Rect bounds) {
+ if (equivalentRequestedOverrideBounds(bounds)) {
return BOUNDS_CHANGE_NONE;
}
int boundsChange = BOUNDS_CHANGE_NONE;
- final Rect existingBounds = getOverrideBounds();
+ final Rect existingBounds = getRequestedOverrideBounds();
if (bounds == null || existingBounds.left != bounds.left
|| existingBounds.top != bounds.top) {
@@ -296,15 +329,16 @@
return mFullConfiguration.windowConfiguration.getWindowingMode();
}
- public int getOverrideWindowingMode() {
- return mOverrideConfiguration.windowConfiguration.getWindowingMode();
+ /** Returns the windowing mode override that is requested by this container. */
+ public int getRequestedOverrideWindowingMode() {
+ return mRequestedOverrideConfiguration.windowConfiguration.getWindowingMode();
}
- /** Sets the windowing mode for the configuration container. */
+ /** Sets the requested windowing mode override for the configuration container. */
public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
- mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
mTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
- onOverrideConfigurationChanged(mTmpConfig);
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
}
/** Sets the always on top flag for this configuration container.
@@ -314,9 +348,16 @@
* - {@Link ActivityDisplay#positionChildAtTop(ActivityStack)};
* */
public void setAlwaysOnTop(boolean alwaysOnTop) {
- mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
mTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
- onOverrideConfigurationChanged(mTmpConfig);
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
+ }
+
+ /** Sets the windowing mode for the configuration container. */
+ void setDisplayWindowingMode(int windowingMode) {
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
}
/**
@@ -386,9 +427,9 @@
throw new IllegalStateException("Can't change activity type once set: " + this
+ " activityType=" + activityTypeToString(activityType));
}
- mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
mTmpConfig.windowConfiguration.setActivityType(activityType);
- onOverrideConfigurationChanged(mTmpConfig);
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
}
public boolean isActivityTypeHome() {
@@ -465,7 +506,7 @@
return;
}
mChangeListeners.add(listener);
- listener.onOverrideConfigurationChanged(mOverrideConfiguration);
+ listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
}
public void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
@@ -501,7 +542,7 @@
public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
final long token = proto.start(fieldId);
if (!trim || mHasOverrideConfiguration) {
- mOverrideConfiguration.writeToProto(proto, OVERRIDE_CONFIGURATION);
+ mRequestedOverrideConfiguration.writeToProto(proto, OVERRIDE_CONFIGURATION);
}
if (!trim) {
mFullConfiguration.writeToProto(proto, FULL_CONFIGURATION);
@@ -519,7 +560,7 @@
pw.println(getName()
+ " type=" + activityTypeToString(getActivityType())
+ " mode=" + windowingModeToString(getWindowingMode())
- + " override-mode=" + windowingModeToString(getOverrideWindowingMode()));
+ + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode()));
for (int i = getChildCount() - 1; i >= 0; --i) {
final E cc = getChildAt(i);
pw.print(childPrefix + "#" + i + " ");
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
index ff14d97..dc4939d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
@@ -23,5 +23,6 @@
*/
public interface ConfigurationContainerListener {
- void onOverrideConfigurationChanged(Configuration overrideConfiguration);
+ /** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */
+ void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 05e8267..2f4c5ca 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -59,8 +59,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -207,6 +210,9 @@
/** Unique identifier of this stack. */
private final int mDisplayId;
+ // TODO: Remove once unification is complete.
+ ActivityDisplay mAcitvityDisplay;
+
/** The containers below are the only child containers the display can have. */
// Contains all window containers that are related to apps (Activities)
private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
@@ -227,7 +233,6 @@
private WindowState mTmpWindow;
private WindowState mTmpWindow2;
- private WindowAnimator mTmpWindowAnimator;
private boolean mTmpRecoveringMemory;
private boolean mUpdateImeTarget;
private boolean mTmpInitial;
@@ -827,12 +832,12 @@
* initialize direct children.
* @param display May not be null.
* @param service You know.
- * @param controller The controller for the display container.
+ * @param activityDisplay The ActivityDisplay for the display container.
*/
DisplayContent(Display display, WindowManagerService service,
- DisplayWindowController controller) {
+ ActivityDisplay activityDisplay) {
super(service);
- setController(controller);
+ mAcitvityDisplay = activityDisplay;
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -1020,11 +1025,6 @@
}
@Override
- DisplayWindowController getController() {
- return (DisplayWindowController) super.getController();
- }
-
- @Override
public Display getDisplay() {
return mDisplay;
}
@@ -1138,6 +1138,17 @@
return true;
}
+ /**
+ * The display content may have configuration set from {@link #DisplayWindowSettings}. This
+ * callback let the owner of container know there is existing configuration to prevent the
+ * values from being replaced by the initializing {@link #ActivityDisplay}.
+ */
+ void initializeDisplayOverrideConfiguration() {
+ if (mAcitvityDisplay != null) {
+ mAcitvityDisplay.onInitializeOverrideConfiguration(getRequestedOverrideConfiguration());
+ }
+ }
+
/** Notify the configuration change of this display. */
void sendNewConfiguration() {
mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
@@ -1546,6 +1557,7 @@
final int dh = displayInfo.logicalHeight;
config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
config.windowConfiguration.setWindowingMode(getWindowingMode());
+ config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
config.windowConfiguration.setRotation(displayInfo.rotation);
final float density = mDisplayMetrics.density;
@@ -1953,6 +1965,17 @@
mWmService.mWindowsChanged = true;
}
+ @Override
+ public void setWindowingMode(int windowingMode) {
+ super.setWindowingMode(windowingMode);
+ super.setDisplayWindowingMode(windowingMode);
+ }
+
+ @Override
+ void setDisplayWindowingMode(int windowingMode) {
+ setWindowingMode(windowingMode);
+ }
+
/**
* In split-screen mode we process the IME containers above the docked divider
* rather than directly above their target.
@@ -4674,6 +4697,11 @@
}
void prepareAppTransition(@WindowManager.TransitionType int transit,
+ boolean alwaysKeepCurrent) {
+ prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
+ }
+
+ void prepareAppTransition(@WindowManager.TransitionType int transit,
boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
boolean forceOverride) {
final boolean prepared = mAppTransition.prepareAppTransitionLocked(
@@ -4725,6 +4753,14 @@
pendingLayoutChanges |= changes;
}
+ /** Check if pending app transition is for activity / task launch. */
+ boolean isNextTransitionForward() {
+ final int transit = mAppTransition.getAppTransition();
+ return transit == TRANSIT_ACTIVITY_OPEN
+ || transit == TRANSIT_TASK_OPEN
+ || transit == TRANSIT_TASK_TO_FRONT;
+ }
+
/**
* @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
*/
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 581cec9..8b8cadc 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -222,6 +222,7 @@
private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private volatile boolean mHdmiPlugged;
+ private volatile boolean mHasStatusBar;
private volatile boolean mHasNavigationBar;
// Can the navigation bar ever move to the side?
private volatile boolean mNavigationBarCanMove;
@@ -523,6 +524,7 @@
mNavigationBarCanMove = width != height && shortSizeDp < 600;
if (mDisplayContent.isDefaultDisplay) {
+ mHasStatusBar = true;
mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
// Allow a system property to override this. Used by the emulator.
@@ -534,6 +536,7 @@
mHasNavigationBar = true;
}
} else {
+ mHasStatusBar = false;
mHasNavigationBar = mDisplayContent.getDisplay().supportsSystemDecorations();
}
}
@@ -589,6 +592,10 @@
return mHasNavigationBar;
}
+ public boolean hasStatusBar() {
+ return mHasStatusBar;
+ }
+
public boolean navigationBarCanMove() {
return mNavigationBarCanMove;
}
@@ -756,6 +763,12 @@
|| attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
}
+ // Accessibility users may need longer timeout duration. This api compares
+ // original timeout with user's preference and return longer one. It returns
+ // original timeout if there's no preference.
+ attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis(
+ (int) attrs.hideTimeoutMilliseconds,
+ AccessibilityManager.FLAG_CONTENT_TEXT);
attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
break;
}
@@ -2493,12 +2506,19 @@
final int landscapeRotation = displayRotation.getLandscapeRotation();
final int seascapeRotation = displayRotation.getSeascapeRotation();
- mStatusBarHeightForRotation[portraitRotation] =
- mStatusBarHeightForRotation[upsideDownRotation] =
- res.getDimensionPixelSize(R.dimen.status_bar_height_portrait);
- mStatusBarHeightForRotation[landscapeRotation] =
- mStatusBarHeightForRotation[seascapeRotation] =
- res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+ if (hasStatusBar()) {
+ mStatusBarHeightForRotation[portraitRotation] =
+ mStatusBarHeightForRotation[upsideDownRotation] =
+ res.getDimensionPixelSize(R.dimen.status_bar_height_portrait);
+ mStatusBarHeightForRotation[landscapeRotation] =
+ mStatusBarHeightForRotation[seascapeRotation] =
+ res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+ } else {
+ mStatusBarHeightForRotation[portraitRotation] =
+ mStatusBarHeightForRotation[upsideDownRotation] =
+ mStatusBarHeightForRotation[landscapeRotation] =
+ mStatusBarHeightForRotation[seascapeRotation] = 0;
+ }
// Height of the navigation bar when presented horizontally at bottom
mNavigationBarHeightForRotationDefault[portraitRotation] =
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
deleted file mode 100644
index 55b3def..0000000
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.util.Slog;
-import android.view.AppTransitionAnimationSpec;
-import android.view.Display;
-import android.view.WindowManager;
-import android.view.WindowManager.TransitionType;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Controller for the display container. This is created by activity manager to link activity
- * displays to the display content they use in window manager.
- */
-public class DisplayWindowController
- extends WindowContainerController<DisplayContent, WindowContainerListener> {
-
- private final int mDisplayId;
-
- public DisplayWindowController(Display display, WindowContainerListener listener) {
- super(listener, WindowManagerService.getInstance());
- mDisplayId = display.getDisplayId();
-
- synchronized (mGlobalLock) {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- mRoot.createDisplayContent(display, this /* controller */);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
-
- if (mContainer == null) {
- throw new IllegalArgumentException("Trying to add display=" + display
- + " dc=" + mRoot.getDisplayContent(mDisplayId));
- }
- }
- }
-
- @VisibleForTesting
- public DisplayWindowController(Display display, WindowManagerService service) {
- super(null, service);
- mDisplayId = display.getDisplayId();
- }
-
- @Override
- public void removeContainer() {
- synchronized (mGlobalLock) {
- if(mContainer == null) {
- if (DEBUG_DISPLAY) Slog.i(TAG_WM, "removeDisplay: could not find displayId="
- + mDisplayId);
- return;
- }
- mContainer.removeIfPossible();
- super.removeContainer();
- }
- }
-
- @Override
- public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration,
- mContainer);
- }
- }
- }
-
- /**
- * Updates the docked/pinned controller resources to the current system context.
- */
- public void preOnConfigurationChanged() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.preOnConfigurationChanged();
- }
- }
- }
-
- /**
- * @see DisplayContent#applyRotationLocked(int, int)
- */
- public void applyRotation(int oldRotation, int newRotation) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.applyRotationLocked(oldRotation, newRotation);
- }
- }
- }
-
- public int getDisplayId() {
- return mDisplayId;
- }
-
- /**
- * Called when the corresponding display receives
- * {@link android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged(int)}.
- */
- public void onDisplayChanged() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- if (DEBUG_DISPLAY) Slog.i(TAG_WM, "onDisplayChanged: could not find display="
- + mDisplayId);
- return;
- }
- mContainer.updateDisplayInfo();
- mService.mWindowPlacerLocked.requestTraversal();
- }
- }
-
- /**
- * Positions the task stack at the given position in the task stack container.
- */
- public void positionChildAt(StackWindowController child, int position,
- boolean includingParents) {
- synchronized (mGlobalLock) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child
- + " at " + position);
- if (mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "positionTaskStackAt: could not find display=" + mContainer);
- return;
- }
- if (child.mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "positionTaskStackAt: could not find stack=" + this);
- return;
- }
- mContainer.positionStackAt(position, child.mContainer, includingParents);
- }
- }
-
- /**
- * Starts deferring the ability to update the IME target. This is needed when a call will
- * attempt to update the IME target before all information about the Windows have been updated.
- */
- public void deferUpdateImeTarget() {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(mDisplayId);
- if (dc != null) {
- dc.deferUpdateImeTarget();
- }
- }
- }
-
- /**
- * Resumes updating the IME target after deferring. See {@link #deferUpdateImeTarget()}
- */
- public void continueUpdateImeTarget() {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(mDisplayId);
- if (dc != null) {
- dc.continueUpdateImeTarget();
- }
- }
- }
-
- /**
- * Sets a focused app on this display.
- *
- * @param token Specifies which app should be focused.
- * @param moveFocusNow Specifies if we should update the focused window immediately.
- */
- public void setFocusedApp(IBinder token, boolean moveFocusNow) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "setFocusedApp: could not find displayId="
- + mDisplayId);
- return;
- }
- final AppWindowToken newFocus;
- if (token == null) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
- + mDisplayId);
- newFocus = null;
- } else {
- newFocus = mRoot.getAppWindowToken(token);
- if (newFocus == null) {
- Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
- + ", displayId=" + mDisplayId);
- }
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
- + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
- }
-
- final boolean changed = mContainer.setFocusedApp(newFocus);
- if (moveFocusNow && changed) {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
- true /*updateInputWindows*/);
- }
- }
- }
-
- public void prepareAppTransition(@WindowManager.TransitionType int transit,
- boolean alwaysKeepCurrent) {
- prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
- }
-
- /**
- * @param transit What kind of transition is happening. Use one of the constants
- * AppTransition.TRANSIT_*.
- * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT
- * be set.
- * @param flags Additional flags for the app transition, Use a combination of the constants
- * AppTransition.TRANSIT_FLAG_*.
- * @param forceOverride Always override the transit, not matter what was set previously.
- */
- public void prepareAppTransition(@WindowManager.TransitionType int transit,
- boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
- boolean forceOverride) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId).prepareAppTransition(transit, alwaysKeepCurrent,
- flags, forceOverride);
- }
- }
-
- public void executeAppTransition() {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId).executeAppTransition();
- }
- }
-
- public void overridePendingAppTransition(String packageName,
- int enterAnim, int exitAnim, IRemoteCallback startedCallback) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransition(
- packageName, enterAnim, exitAnim, startedCallback);
- }
- }
-
- public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
- int startHeight) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransitionScaleUp(
- startX, startY, startWidth, startHeight);
- }
- }
-
- public void overridePendingAppTransitionClipReveal(int startX, int startY,
- int startWidth, int startHeight) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId)
- .mAppTransition.overridePendingAppTransitionClipReveal(startX, startY,
- startWidth, startHeight);
- }
- }
-
- public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX,
- int startY, IRemoteCallback startedCallback, boolean scaleUp) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId)
- .mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY,
- startedCallback, scaleUp);
- }
- }
-
- public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX,
- int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
- boolean scaleUp) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId)
- .mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
- startY, targetWidth, targetHeight, startedCallback, scaleUp);
- }
- }
-
- public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
- IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
- boolean scaleUp) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId)
- .mAppTransition.overridePendingAppTransitionMultiThumb(specs,
- onAnimationStartedCallback, onAnimationFinishedCallback, scaleUp);
- }
- }
-
- public void overridePendingAppTransitionStartCrossProfileApps() {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId)
- .mAppTransition.overridePendingAppTransitionStartCrossProfileApps();
- }
- }
-
- public void overridePendingAppTransitionInPlace(String packageName, int anim) {
- synchronized (mGlobalLock) {
- mRoot.getDisplayContent(mDisplayId)
- .mAppTransition.overrideInPlaceAppTransition(packageName, anim);
- }
- }
-
- /**
- * Get Pending App transition of display.
- *
- * @return The pending app transition of the display.
- */
- public @TransitionType int getPendingAppTransition() {
- synchronized (mGlobalLock) {
- return mRoot.getDisplayContent(mDisplayId).mAppTransition.getAppTransition();
- }
- }
-
- /**
- * Check if pending app transition is for activity / task launch.
- */
- public boolean isNextTransitionForward() {
- final int transit = getPendingAppTransition();
- return transit == TRANSIT_ACTIVITY_OPEN
- || transit == TRANSIT_TASK_OPEN
- || transit == TRANSIT_TASK_TO_FRONT;
- }
-
- /**
- * Checks if system decorations should be shown on this display.
- *
- * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
- */
- public boolean supportsSystemDecorations() {
- synchronized (mGlobalLock) {
- return mContainer.supportsSystemDecorations();
- }
- }
-
- @Override
- public String toString() {
- return "{DisplayWindowController displayId=" + mDisplayId + "}";
- }
-}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 639ed02..f9c9d33 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,5 +1,6 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -9,7 +10,6 @@
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
-import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
@@ -204,6 +204,37 @@
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
+ /** Callback to get pointer display id. */
+ @Override
+ public int getPointerDisplayId() {
+ synchronized (mService.mGlobalLock) {
+ // If desktop mode is not enabled, show on the default display.
+ if (!mService.mForceDesktopModeOnExternalDisplays) {
+ return DEFAULT_DISPLAY;
+ }
+
+ // Look for the topmost freeform display.
+ int firstExternalDisplayId = DEFAULT_DISPLAY;
+ for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
+ // Heuristic solution here. Currently when "Freeform windows" developer option is
+ // enabled we automatically put secondary displays in freeform mode and emulating
+ // "desktop mode". It also makes sense to show the pointer on the same display.
+ if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ return displayContent.getDisplayId();
+ }
+
+ if (firstExternalDisplayId == DEFAULT_DISPLAY
+ && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
+ firstExternalDisplayId = displayContent.getDisplayId();
+ }
+ }
+
+ // Look for the topmost non-default display
+ return firstExternalDisplayId;
+ }
+ }
+
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 88b22cb..c1e9a73 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -194,7 +194,7 @@
final boolean hasFocus, final boolean hasWallpaper) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
- flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags);
+ flags = child.getSurfaceTouchableRegion(inputWindowHandle.touchableRegion, flags);
inputWindowHandle.layoutParamsFlags = flags;
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 4ef3513..5f56fe5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -174,7 +174,7 @@
mWindowManager.deferSurfaceLayout();
try {
setKeyguardGoingAway(true);
- mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
+ mRootActivityContainer.getDefaultDisplay().mDisplayContent
.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
false /* alwaysKeepCurrent */, convertTransitFlags(flags),
false /* forceOverride */);
@@ -302,7 +302,7 @@
if (isKeyguardLocked()) {
mWindowManager.deferSurfaceLayout();
try {
- mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
+ mRootActivityContainer.getDefaultDisplay().mDisplayContent
.prepareAppTransition(resolveOccludeTransit(),
false /* alwaysKeepCurrent */, 0 /* flags */,
true /* forceOverride */);
@@ -332,11 +332,11 @@
// If we are about to unocclude the Keyguard, but we can dismiss it without security,
// we immediately dismiss the Keyguard so the activity gets shown without a flicker.
- final DisplayWindowController dwc =
- mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+ final DisplayContent dc =
+ mRootActivityContainer.getDefaultDisplay().mDisplayContent;
if (mKeyguardShowing && canDismissKeyguard()
- && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
- dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+ && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+ dc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
0 /* flags */, true /* forceOverride */);
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
@@ -355,10 +355,10 @@
}
private int resolveOccludeTransit() {
- final DisplayWindowController dwc =
- mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+ final DisplayContent dc =
+ mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent;
if (mBeforeUnoccludeTransit != TRANSIT_UNSET
- && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+ && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
// TODO(b/113840485): Handle app transition for individual display.
&& isDisplayOccluded(DEFAULT_DISPLAY)) {
@@ -369,7 +369,7 @@
} else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
// Save transit in case we dismiss/occlude Keyguard shortly after.
- mBeforeUnoccludeTransit = dwc.getPendingAppTransition();
+ mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransition();
return TRANSIT_KEYGUARD_UNOCCLUDE;
} else {
return TRANSIT_KEYGUARD_OCCLUDE;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index d2f2863..3b66f7d 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -582,7 +582,7 @@
mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
final ActivityStack stack = task.getStack();
if (stack != null) {
- stack.getDisplay().getWindowContainerController().executeAppTransition();
+ stack.getDisplay().mDisplayContent.executeAppTransition();
}
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
index 1c7ebd6..2a05af4 100644
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java
@@ -77,7 +77,7 @@
}
boolean isAnimatingBoundsToFullscreen() {
- return getWindowContainerController().isAnimatingBoundsToFullscreen();
+ return getWindowContainerController().mContainer.isAnimatingBoundsToFullscreen();
}
/**
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index bbdcc62..518e39b 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
@@ -180,15 +181,6 @@
}
/**
- * @return whether the bounds are currently animating to fullscreen.
- */
- public boolean isAnimatingBoundsToFullscreen() {
- synchronized (mGlobalLock) {
- return mContainer.isAnimatingBoundsToFullscreen();
- }
- }
-
- /**
* @return whether the stack can be resized from the bounds animation.
*/
public boolean pinnedStackResizeDisallowed() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 1e287b4..42cd8e8 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -47,7 +47,7 @@
import com.android.server.LocalServices;
import com.android.server.am.AssistDataRequester;
-import com.android.server.intelligence.IntelligenceManagerInternal;
+import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
import java.util.List;
@@ -93,12 +93,12 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
// TODO(multi-display) currently only support recents animation in default display.
- final DisplayWindowController dwc =
- mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+ final DisplayContent dc =
+ mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent;
if (!mWindowManager.canStartRecentsAnimation()) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
- + dwc.getPendingAppTransition());
+ + dc.mAppTransition.getAppTransition());
return;
}
@@ -107,7 +107,7 @@
&& recentsComponent.equals(intent.getComponent())
? ACTIVITY_TYPE_RECENTS
: ACTIVITY_TYPE_HOME;
- final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+ ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
final boolean hasExistingActivity = targetActivity != null;
@@ -153,7 +153,7 @@
"startRecentsActivity");
}
} else {
- // No recents activity
+ // No recents activity, create the new recents activity bottom most
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchActivityType(mTargetActivityType);
options.setAvoidMoveToFront();
@@ -166,11 +166,20 @@
.setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
.setMayWait(userId)
.execute();
+
+ // Move the recents activity into place for the animation
+ targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+ mTargetActivityType).getTopActivity();
+ targetStack = targetActivity.getActivityStack();
+ mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
+ if (DEBUG) {
+ Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
+ + mDefaultDisplay.getStackAbove(targetStack));
+ }
+
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
mWindowManager.executeAppTransition();
- targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
- mTargetActivityType).getTopActivity();
// TODO: Maybe wait for app to draw in this particular case?
@@ -225,8 +234,8 @@
public void onAssistDataReceivedLocked(Bundle data, int activityIndex,
int activityCount) {
// Try to notify the intelligence service first
- final IntelligenceManagerInternal imService =
- LocalServices.getService(IntelligenceManagerInternal.class);
+ final ContentCaptureManagerInternal imService =
+ LocalServices.getService(ContentCaptureManagerInternal.class);
final IBinder activityToken = topActivities.get(activityIndex);
if (imService == null
|| !imService.sendActivityAssistData(userId, activityToken, data)) {
@@ -236,8 +245,8 @@
}
};
} else {
- final IntelligenceManagerInternal imService =
- LocalServices.getService(IntelligenceManagerInternal.class);
+ final ContentCaptureManagerInternal imService =
+ LocalServices.getService(ContentCaptureManagerInternal.class);
if (imService == null) {
// There is no intelligence service, so there is no point requesting assist data
return;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b6609e4..83ba384 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -504,10 +504,12 @@
public void binderDied() {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
- // Clear associated input consumers on runner death
- final InputMonitor inputMonitor =
- mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
- inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ synchronized (mService.getWindowManagerLock()) {
+ // Clear associated input consumers on runner death
+ final InputMonitor inputMonitor =
+ mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+ inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ }
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -603,8 +605,8 @@
mTask = task;
mIsRecentTaskInvisible = isRecentTaskInvisible;
final WindowContainer container = mTask.getParent();
- container.getRelativePosition(mPosition);
- container.getBounds(mBounds);
+ container.getRelativeDisplayedPosition(mPosition);
+ mBounds.set(container.getDisplayedBounds());
}
RemoteAnimationTarget createRemoteAnimationApp() {
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 1d86b46..c5b42f9 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -864,7 +864,7 @@
// Resize the pinned stack to match the current size of the task the activity we are
// going to be moving is currently contained in. We do this to have the right starting
// animation bounds for the pinned stack to the desired bounds the caller wants.
- resizeStack(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
+ resizeStack(stack, task.getRequestedOverrideBounds(), null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
@@ -914,7 +914,7 @@
void executeAppTransitionForAllDisplay() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- display.getWindowContainerController().executeAppTransition();
+ display.mDisplayContent.executeAppTransition();
}
}
@@ -1254,9 +1254,8 @@
}
// TODO: remove after object merge with RootWindowContainer
- void onChildPositionChanged(DisplayWindowController childController, int position) {
+ void onChildPositionChanged(ActivityDisplay display, int position) {
// Assume AM lock is held from positionChildAt of controller in each hierarchy.
- final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
if (display != null) {
positionChildAt(display, position);
}
@@ -1281,8 +1280,7 @@
@VisibleForTesting
void addChild(ActivityDisplay activityDisplay, int position) {
positionChildAt(activityDisplay, position);
- mRootWindowContainer.positionChildAt(position,
- activityDisplay.getWindowContainerController().mContainer);
+ mRootWindowContainer.positionChildAt(position, activityDisplay.mDisplayContent);
}
void removeChild(ActivityDisplay activityDisplay) {
@@ -1297,7 +1295,7 @@
throw new IllegalArgumentException("No display found with id: " + displayId);
}
- return activityDisplay.getOverrideConfiguration();
+ return activityDisplay.getRequestedOverrideConfiguration();
}
void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
@@ -1306,7 +1304,7 @@
throw new IllegalArgumentException("No display found with id: " + displayId);
}
- activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
+ activityDisplay.onRequestedOverrideConfigurationChanged(overrideConfiguration);
}
void prepareForShutdown() {
@@ -2161,7 +2159,7 @@
for (int i = 0; i < displayCount; i++) {
final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
- pw.println(activityDisplay.getOverrideConfiguration());
+ pw.println(activityDisplay.getRequestedOverrideConfiguration());
}
}
@@ -2190,7 +2188,7 @@
+ ": type=" + activityTypeToString(stack.getActivityType())
+ " mode=" + windowingModeToString(stack.getWindowingMode()));
pw.println(" isSleeping=" + stack.shouldSleepActivities());
- pw.println(" mBounds=" + stack.getOverrideBounds());
+ pw.println(" mBounds=" + stack.getRequestedOverrideBounds());
printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
needSep);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b98d154..3bbef92 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -224,7 +224,7 @@
return null;
}
- DisplayContent createDisplayContent(final Display display, DisplayWindowController controller) {
+ DisplayContent createDisplayContent(final Display display, ActivityDisplay activityDisplay) {
final int displayId = display.getDisplayId();
// In select scenarios, it is possible that a DisplayContent will be created on demand
@@ -233,17 +233,17 @@
final DisplayContent existing = getDisplayContent(displayId);
if (existing != null) {
- initializeDisplayOverrideConfiguration(controller, existing);
- existing.setController(controller);
+ existing.mAcitvityDisplay = activityDisplay;
+ existing.initializeDisplayOverrideConfiguration();
return existing;
}
- final DisplayContent dc = new DisplayContent(display, mWmService, controller);
+ final DisplayContent dc = new DisplayContent(display, mWmService, activityDisplay);
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
- initializeDisplayOverrideConfiguration(controller, dc);
+ dc.initializeDisplayOverrideConfiguration();
if (mWmService.mDisplayManagerInternal != null) {
mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
@@ -256,19 +256,6 @@
return dc;
}
- /**
- * The display content may have configuration set from {@link #DisplayWindowSettings}. This
- * callback let the owner of container know there is existing configuration to prevent the
- * values from being replaced by the initializing {@link #ActivityDisplay}.
- */
- private void initializeDisplayOverrideConfiguration(DisplayWindowController controller,
- DisplayContent displayContent) {
- if (controller != null && controller.mListener != null) {
- controller.mListener.onInitializeOverrideConfiguration(
- displayContent.getOverrideConfiguration());
- }
- }
-
boolean isLayoutNeeded() {
final int numDisplays = mChildren.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@@ -356,13 +343,13 @@
void setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration,
@NonNull DisplayContent displayContent) {
- final Configuration currentConfig = displayContent.getOverrideConfiguration();
+ final Configuration currentConfig = displayContent.getRequestedOverrideConfiguration();
final boolean configChanged = currentConfig.diff(newConfiguration) != 0;
if (!configChanged) {
return;
}
- displayContent.onOverrideConfigurationChanged(newConfiguration);
+ displayContent.onRequestedOverrideConfigurationChanged(newConfiguration);
if (displayContent.getDisplayId() == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config. In this case
@@ -1040,7 +1027,7 @@
void positionChildAt(int position, DisplayContent child, boolean includingParents) {
super.positionChildAt(position, child, includingParents);
if (mRootActivityContainer != null) {
- mRootActivityContainer.onChildPositionChanged(child.getController(), position);
+ mRootActivityContainer.onChildPositionChanged(child.mAcitvityDisplay, position);
}
}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 35264a2..ada807b 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -21,7 +21,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
@@ -29,8 +28,6 @@
import android.os.Message;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,11 +46,7 @@
private final H mHandler;
- // Temp bounds only used in adjustConfigurationForBounds()
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpStableInsets = new Rect();
- private final Rect mTmpNonDecorInsets = new Rect();
- private final Rect mTmpDisplayBounds = new Rect();
+ final Rect mTmpBounds = new Rect();
public StackWindowController(int stackId, StackWindowListener listener, int displayId,
boolean onTop, Rect outBounds) {
@@ -67,107 +60,87 @@
mStackId = stackId;
mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- throw new IllegalArgumentException("Trying to add stackId=" + stackId
- + " to unknown displayId=" + displayId);
- }
-
- dc.createStack(stackId, onTop, this);
- getRawBounds(outBounds);
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ throw new IllegalArgumentException("Trying to add stackId=" + stackId
+ + " to unknown displayId=" + displayId);
}
+
+ dc.createStack(stackId, onTop, this);
+ getRawBounds(outBounds);
}
@Override
public void removeContainer() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.removeIfPossible();
- super.removeContainer();
- }
+ if (mContainer != null) {
+ mContainer.removeIfPossible();
+ super.removeContainer();
}
}
- public void reparent(int displayId, Rect outStackBounds, boolean onTop) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
- + " to displayId=" + displayId);
- }
-
- final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
- if (targetDc == null) {
- throw new IllegalArgumentException("Trying to move stackId=" + mStackId
- + " to unknown displayId=" + displayId);
- }
-
- targetDc.moveStackToDisplay(mContainer, onTop);
- getRawBounds(outStackBounds);
+ void reparent(int displayId, Rect outStackBounds, boolean onTop) {
+ if (mContainer == null) {
+ throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
+ + " to displayId=" + displayId);
}
+
+ final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
+ if (targetDc == null) {
+ throw new IllegalArgumentException("Trying to move stackId=" + mStackId
+ + " to unknown displayId=" + displayId);
+ }
+
+ targetDc.moveStackToDisplay(mContainer, onTop);
+ getRawBounds(outStackBounds);
}
- public void positionChildAt(TaskWindowContainerController child, int position) {
- synchronized (mGlobalLock) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
- + " at " + position);
- if (child.mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "positionChildAt: could not find task=" + this);
- return;
- }
- if (mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "positionChildAt: could not find stack for task=" + mContainer);
- return;
- }
- child.mContainer.positionAt(position);
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ void positionChildAt(Task child, int position) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
}
+ if (child == null) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
+ }
+ return;
+ }
+ if (mContainer == null) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: could not find stack for task=" + mContainer);
+ }
+ return;
+ }
+ child.positionAt(position);
+ mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
- public void positionChildAtTop(TaskWindowContainerController child, boolean includingParents) {
+ void positionChildAtTop(Task child, boolean includingParents) {
if (child == null) {
// TODO: Fix the call-points that cause this to happen.
return;
}
- synchronized (mGlobalLock) {
- final Task childTask = child.mContainer;
- if (childTask == null) {
- Slog.e(TAG_WM, "positionChildAtTop: task=" + child + " not found");
- return;
- }
- mContainer.positionChildAt(POSITION_TOP, childTask, includingParents);
+ mContainer.positionChildAt(POSITION_TOP, child, includingParents);
- final DisplayContent displayContent = mContainer.getDisplayContent();
- if (displayContent.mAppTransition.isTransitionSet()) {
- childTask.setSendingToBottom(false);
- }
- displayContent.layoutAndAssignWindowLayersIfNeeded();
+ final DisplayContent displayContent = mContainer.getDisplayContent();
+ if (displayContent.mAppTransition.isTransitionSet()) {
+ child.setSendingToBottom(false);
}
+ displayContent.layoutAndAssignWindowLayersIfNeeded();
}
- public void positionChildAtBottom(TaskWindowContainerController child,
- boolean includingParents) {
+ void positionChildAtBottom(Task child, boolean includingParents) {
if (child == null) {
// TODO: Fix the call-points that cause this to happen.
return;
}
- synchronized (mGlobalLock) {
- final Task childTask = child.mContainer;
- if (childTask == null) {
- Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
- return;
- }
- mContainer.positionChildAt(POSITION_BOTTOM, childTask, includingParents);
+ mContainer.positionChildAt(POSITION_BOTTOM, child, includingParents);
- if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) {
- childTask.setSendingToBottom(true);
- }
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) {
+ child.setSendingToBottom(true);
}
+ mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
/**
@@ -179,24 +152,20 @@
*/
public void resize(Rect bounds, SparseArray<Rect> taskBounds,
SparseArray<Rect> taskTempInsetBounds) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
- }
- // We might trigger a configuration change. Save the current task bounds for freezing.
- mContainer.prepareFreezingTaskBounds();
- if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
- && mContainer.isVisible()) {
- mContainer.getDisplayContent().setLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
+ if (mContainer == null) {
+ throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
+ }
+ // We might trigger a configuration change. Save the current task bounds for freezing.
+ mContainer.prepareFreezingTaskBounds();
+ if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
+ && mContainer.isVisible()) {
+ mContainer.getDisplayContent().setLayoutNeeded();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
}
}
public void onPipAnimationEndResize() {
- synchronized (mService.mGlobalLock) {
- mContainer.onPipAnimationEndResize();
- }
+ mContainer.onPipAnimationEndResize();
}
/**
@@ -205,169 +174,37 @@
public void getStackDockedModeBounds(Configuration parentConfig, Rect dockedBounds,
Rect currentTempTaskBounds,
Rect outStackBounds, Rect outTempTaskBounds) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getStackDockedModeBoundsLocked(parentConfig, dockedBounds,
- currentTempTaskBounds, outStackBounds, outTempTaskBounds);
- return;
- }
- outStackBounds.setEmpty();
- outTempTaskBounds.setEmpty();
+ if (mContainer != null) {
+ mContainer.getStackDockedModeBoundsLocked(parentConfig, dockedBounds,
+ currentTempTaskBounds, outStackBounds, outTempTaskBounds);
+ return;
}
+ outStackBounds.setEmpty();
+ outTempTaskBounds.setEmpty();
}
public void prepareFreezingTaskBounds() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
- + " not found.");
- }
- mContainer.prepareFreezingTaskBounds();
+ if (mContainer == null) {
+ throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
+ + " not found.");
}
+ mContainer.prepareFreezingTaskBounds();
}
public void getRawBounds(Rect outBounds) {
- synchronized (mGlobalLock) {
- if (mContainer.matchParentBounds()) {
- outBounds.setEmpty();
- } else {
- mContainer.getRawBounds(outBounds);
- }
+ if (mContainer.matchParentBounds()) {
+ outBounds.setEmpty();
+ } else {
+ mContainer.getRawBounds(outBounds);
}
}
public void getBounds(Rect outBounds) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getBounds(outBounds);
- return;
- }
- outBounds.setEmpty();
+ if (mContainer != null) {
+ mContainer.getBounds(outBounds);
+ return;
}
- }
-
- /**
- * Adjusts the screen size in dp's for the {@param config} for the given params. The provided
- * params represent the desired state of a configuration change. Since this utility is used
- * before mContainer has been updated, any relevant properties (like {@param windowingMode})
- * need to be passed in.
- */
- public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
- Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
- boolean overrideHeight, float density, Configuration config,
- Configuration parentConfig, int windowingMode) {
- synchronized (mGlobalLock) {
- final TaskStack stack = mContainer;
- final DisplayContent displayContent = stack.getDisplayContent();
- final DisplayInfo di = displayContent.getDisplayInfo();
- final DisplayCutout displayCutout = di.displayCutout;
- final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
-
- // Get the insets and display bounds
- displayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- displayCutout, mTmpStableInsets);
- displayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- displayCutout, mTmpNonDecorInsets);
- mTmpDisplayBounds.set(0, 0, di.logicalWidth, di.logicalHeight);
-
- int width;
- int height;
-
- final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
-
- config.windowConfiguration.setBounds(bounds);
- config.windowConfiguration.setAppBounds(!bounds.isEmpty() ? bounds : null);
- boolean intersectParentBounds = false;
-
- if (WindowConfiguration.isFloating(windowingMode)) {
- // Floating tasks should not be resized to the screen's bounds.
-
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED
- && bounds.width() == mTmpDisplayBounds.width()
- && bounds.height() == mTmpDisplayBounds.height()) {
- // If the bounds we are animating is the same as the fullscreen stack
- // dimensions, then apply the same inset calculations that we normally do for
- // the fullscreen stack, without intersecting it with the display bounds
- stableBounds.inset(mTmpStableInsets);
- nonDecorBounds.inset(mTmpNonDecorInsets);
- // Move app bounds to zero to apply intersection with parent correctly. They are
- // used only for evaluating width and height, so it's OK to move them around.
- config.windowConfiguration.getAppBounds().offsetTo(0, 0);
- intersectParentBounds = true;
- }
- width = (int) (stableBounds.width() / density);
- height = (int) (stableBounds.height() / density);
- } else {
- // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
- // area, i.e. the screen area without the system bars.
- // Additionally task dimensions should not be bigger than its parents dimensions.
- // The non decor inset are areas that could never be removed in Honeycomb. See
- // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
- intersectDisplayBoundsExcludeInsets(nonDecorBounds,
- insetBounds != null ? insetBounds : bounds, mTmpNonDecorInsets,
- mTmpDisplayBounds, overrideWidth, overrideHeight);
- intersectDisplayBoundsExcludeInsets(stableBounds,
- insetBounds != null ? insetBounds : bounds, mTmpStableInsets,
- mTmpDisplayBounds, overrideWidth, overrideHeight);
- width = Math.min((int) (stableBounds.width() / density),
- parentConfig.screenWidthDp);
- height = Math.min((int) (stableBounds.height() / density),
- parentConfig.screenHeightDp);
- intersectParentBounds = true;
- }
-
- if (intersectParentBounds && config.windowConfiguration.getAppBounds() != null) {
- config.windowConfiguration.getAppBounds().intersect(parentAppBounds);
- }
-
- config.screenWidthDp = width;
- config.screenHeightDp = height;
- config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
- insetBounds != null ? insetBounds : bounds, density, windowingMode);
- }
- }
-
- /**
- * Intersects the specified {@code inOutBounds} with the display frame that excludes the stable
- * inset areas.
- *
- * @param inOutBounds The inOutBounds to subtract the stable inset areas from.
- */
- private void intersectDisplayBoundsExcludeInsets(Rect inOutBounds, Rect inInsetBounds,
- Rect stableInsets, Rect displayBounds, boolean overrideWidth, boolean overrideHeight) {
- mTmpRect.set(inInsetBounds);
- mService.intersectDisplayInsetBounds(displayBounds, stableInsets, mTmpRect);
- int leftInset = mTmpRect.left - inInsetBounds.left;
- int topInset = mTmpRect.top - inInsetBounds.top;
- int rightInset = overrideWidth ? 0 : inInsetBounds.right - mTmpRect.right;
- int bottomInset = overrideHeight ? 0 : inInsetBounds.bottom - mTmpRect.bottom;
- inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
- }
-
- /**
- * Calculates the smallest width for a task given the target {@param bounds} and
- * {@param windowingMode}. Avoid using values from mContainer since they can be out-of-date.
- *
- * @return the smallest width to be used in the Configuration, in dips
- */
- private int getSmallestWidthForTaskBounds(Rect bounds, float density, int windowingMode) {
- final DisplayContent displayContent = mContainer.getDisplayContent();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-
- if (bounds == null || (bounds.width() == displayInfo.logicalWidth &&
- bounds.height() == displayInfo.logicalHeight)) {
- // If the bounds are fullscreen, return the value of the fullscreen configuration
- return displayContent.getConfiguration().smallestScreenWidthDp;
- } else if (WindowConfiguration.isFloating(windowingMode)) {
- // For floating tasks, calculate the smallest width from the bounds of the task
- return (int) (Math.min(bounds.width(), bounds.height()) / density);
- } else {
- // Iterating across all screen orientations, and return the minimum of the task
- // width taking into account that the bounds might change because the snap algorithm
- // snaps to a different value
- return displayContent.getDockedDividerController()
- .getSmallestWidthDpForBounds(bounds);
- }
+ outBounds.setEmpty();
}
void requestResize(Rect bounds) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d3db5df..b10fd31 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -24,20 +24,22 @@
import static android.content.res.Configuration.EMPTY;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
+import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.ID;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.CallSuper;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -54,7 +56,7 @@
import java.io.PrintWriter;
import java.util.function.Consumer;
-class Task extends WindowContainer<AppWindowToken> {
+class Task extends WindowContainer<AppWindowToken> implements ConfigurationContainerListener{
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
// TODO: Track parent marks like this in WindowContainer.
@@ -66,8 +68,8 @@
final Rect mPreparedFrozenBounds = new Rect();
final Configuration mPreparedFrozenMergedConfig = new Configuration();
- // Bounds used to calculate the insets.
- private final Rect mTempInsetBounds = new Rect();
+ // If non-empty, bounds used to display the task during animations/interactions.
+ private final Rect mOverrideDisplayedBounds = new Rect();
/** ID of the display which rotation {@link #mRotation} has. */
private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
@@ -109,17 +111,25 @@
/** @see #setCanAffectSystemUiFlags */
private boolean mCanAffectSystemUiFlags = true;
+ // TODO: remove after unification
+ TaskRecord mTaskRecord;
+
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
boolean supportsPictureInPicture, TaskDescription taskDescription,
- TaskWindowContainerController controller) {
+ TaskRecord taskRecord) {
super(service);
mTaskId = taskId;
mStack = stack;
mUserId = userId;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
- setController(controller);
- setBounds(getOverrideBounds());
+ mTaskRecord = taskRecord;
+ if (mTaskRecord != null) {
+ // This can be null when we call createTaskInStack in WindowTestUtils. Remove this after
+ // unification.
+ mTaskRecord.registerConfigurationChangeListener(this);
+ }
+ setBounds(getRequestedOverrideBounds());
mTaskDescription = taskDescription;
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
@@ -191,10 +201,28 @@
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
mDeferRemoval = false;
+ if (mTaskRecord != null) {
+ mTaskRecord.unregisterConfigurationChangeListener(this);
+ }
super.removeImmediately();
}
+ void reparent(StackWindowController stackController, int position, boolean moveParents) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
+ + " to stack=" + stackController + " at " + position);
+ }
+ final TaskStack stack = stackController.mContainer;
+ if (stack == null) {
+ throw new IllegalArgumentException("reparent: could not find stack="
+ + stackController);
+ }
+ reparent(stack, position, moveParents);
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+
+
void reparent(TaskStack stack, int position, boolean moveParents) {
if (stack == mStack) {
throw new IllegalArgumentException(
@@ -300,6 +328,12 @@
return boundsChange;
}
+ void resize(boolean relayout, boolean forced) {
+ if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+ }
+
@Override
void onDisplayChanged(DisplayContent dc) {
adjustBoundsForDisplayChangeIfNeeded(dc);
@@ -307,23 +341,23 @@
}
/**
- * Sets the bounds used to calculate the insets. See
- * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
+ * Sets bounds that override where the task is displayed. Used during transient operations
+ * like animation / interaction.
*/
- void setTempInsetBounds(Rect tempInsetBounds) {
- if (tempInsetBounds != null) {
- mTempInsetBounds.set(tempInsetBounds);
+ void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
+ if (overrideDisplayedBounds != null) {
+ mOverrideDisplayedBounds.set(overrideDisplayedBounds);
} else {
- mTempInsetBounds.setEmpty();
+ mOverrideDisplayedBounds.setEmpty();
}
}
/**
- * Gets the bounds used to calculate the insets. See
+ * Gets the bounds that override where the task is displayed. See
* {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
*/
- void getTempInsetBounds(Rect out) {
- out.set(mTempInsetBounds);
+ Rect getOverrideDisplayedBounds() {
+ return mOverrideDisplayedBounds;
}
void setResizeable(int resizeMode) {
@@ -369,7 +403,7 @@
* the adjusted bounds's top.
*/
void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
- if (!isResizeable() || EMPTY.equals(getOverrideConfiguration())) {
+ if (!isResizeable() || EMPTY.equals(getRequestedOverrideConfiguration())) {
return;
}
@@ -380,8 +414,13 @@
} else {
mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
}
- setTempInsetBounds(tempInsetBounds);
- setBounds(mTmpRect2, false /* forced */);
+ if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
+ setOverrideDisplayedBounds(null);
+ setBounds(mTmpRect2);
+ } else {
+ setOverrideDisplayedBounds(mTmpRect2);
+ setBounds(tempInsetBounds);
+ }
}
/** Return true if the current bound can get outputted to the rest of the system as-is. */
@@ -407,6 +446,15 @@
mStack.getDisplayContent().getBounds(out);
}
+ @Override
+ public Rect getDisplayedBounds() {
+ if (mOverrideDisplayedBounds.isEmpty()) {
+ return super.getDisplayedBounds();
+ } else {
+ return mOverrideDisplayedBounds;
+ }
+ }
+
/**
* Calculate the maximum visible area of this task. If the task has only one app,
* the result will be visible frame of that app. If the task has more than one apps,
@@ -501,6 +549,15 @@
return mDragResizeMode;
}
+ /**
+ * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
+ *
+ * @param resizing Whether to put the task into drag resize mode.
+ */
+ public void setTaskDockedResizing(boolean resizing) {
+ setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+ }
+
private void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
if (displayContent == null) {
return;
@@ -542,9 +599,8 @@
displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (setBounds(mTmpRect2) != BOUNDS_CHANGE_NONE) {
- final TaskWindowContainerController controller = getController();
- if (controller != null) {
- controller.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
+ if (mTaskRecord != null) {
+ mTaskRecord.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
}
}
}
@@ -617,6 +673,20 @@
return null;
}
+ void positionChildAtTop(AppWindowToken aToken) {
+ positionChildAt(aToken, POSITION_TOP);
+ }
+
+ void positionChildAt(AppWindowToken aToken, int position) {
+ if (aToken == null) {
+ Slog.w(TAG_WM,
+ "Attempted to position of non-existing app");
+ return;
+ }
+
+ positionChildAt(position, aToken, false /* includeParents */);
+ }
+
boolean isFullscreen() {
if (useCurrentBounds()) {
return matchParentBounds();
@@ -642,6 +712,10 @@
mTaskDescription = taskDescription;
}
+ void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
+ mTaskRecord.onSnapshotChanged(snapshot);
+ }
+
TaskDescription getTaskDescription() {
return mTaskDescription;
}
@@ -652,11 +726,6 @@
}
@Override
- TaskWindowContainerController getController() {
- return (TaskWindowContainerController) super.getController();
- }
-
- @Override
void forAllTasks(Consumer<Task> callback) {
callback.accept(this);
}
@@ -723,7 +792,7 @@
}
proto.write(FILLS_PARENT, matchParentBounds());
getBounds().writeToProto(proto, BOUNDS);
- mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
+ mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
proto.write(DEFER_REMOVAL, mDeferRemoval);
proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
@@ -739,7 +808,7 @@
pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
pw.println(doublePrefix + "mdr=" + mDeferRemoval);
pw.println(doublePrefix + "appTokens=" + mChildren);
- pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
+ pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
final String triplePrefix = doublePrefix + " ";
final String quadruplePrefix = triplePrefix + " ";
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 4ae2a79..1fb7979 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -44,6 +44,7 @@
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Build;
import android.util.Slog;
@@ -526,12 +527,24 @@
adjustBoundsToAvoidConflict(display, inOutBounds);
}
+ private int convertOrientationToScreenOrientation(int orientation) {
+ switch (orientation) {
+ case Configuration.ORIENTATION_LANDSCAPE:
+ return SCREEN_ORIENTATION_LANDSCAPE;
+ case Configuration.ORIENTATION_PORTRAIT:
+ return SCREEN_ORIENTATION_PORTRAIT;
+ default:
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+ }
+
private int resolveOrientation(@NonNull ActivityRecord root, @NonNull ActivityDisplay display,
@NonNull Rect bounds) {
int orientation = resolveOrientation(root);
if (orientation == SCREEN_ORIENTATION_LOCKED) {
- orientation = bounds.isEmpty() ? display.getConfiguration().orientation
+ orientation = bounds.isEmpty()
+ ? convertOrientationToScreenOrientation(display.getConfiguration().orientation)
: orientationFromBounds(bounds);
if (DEBUG) {
appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 30751bc..5bb6440 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -27,6 +27,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
@@ -47,6 +48,7 @@
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.server.EventLogTags.WM_TASK_CREATED;
import static com.android.server.am.TaskRecordProto.ACTIVITIES;
import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
import static com.android.server.am.TaskRecordProto.BOUNDS;
@@ -76,10 +78,15 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static java.lang.Integer.MAX_VALUE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -106,8 +113,10 @@
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
+import android.util.EventLog;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -125,8 +134,7 @@
import java.util.ArrayList;
import java.util.Objects;
-// TODO: Make package private again once move to WM package is complete.
-public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
+class TaskRecord extends ConfigurationContainer {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_ATM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -190,6 +198,13 @@
// Do not move the stack as a part of reparenting
static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+ // The height/width divide used when fitting a task within a bounds with method
+ // {@link #fitWithinBounds}.
+ // We always want the task to to be visible in the bounds without affecting its size when
+ // fitting. To make sure this is the case, we don't adjust the task left or top side pass
+ // the input bounds right or bottom side minus the width or height divided by this value.
+ private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
+
/**
* The factory used to create {@link TaskRecord}. This allows OEM subclass {@link TaskRecord}.
*/
@@ -295,7 +310,8 @@
private final Rect mTmpStableBounds = new Rect();
private final Rect mTmpNonDecorBounds = new Rect();
- private final Rect mTmpRect = new Rect();
+ private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpInsets = new Rect();
// Last non-fullscreen bounds the task was launched in or resized to.
// The information is persisted and used to determine the appropriate stack to launch the
@@ -310,10 +326,16 @@
// This number will be assigned when we evaluate OOM scores for all visible tasks.
int mLayerRank = -1;
+ // When non-empty, this represents the bounds this task will be drawn at. This gets set during
+ // transient operations such as split-divider dragging and animations.
+ // TODO(b/119687367): This member is temporary.
+ final Rect mDisplayedBounds = new Rect();
+
/** Helper object used for updating override configuration. */
private Configuration mTmpConfig = new Configuration();
- private TaskWindowContainerController mWindowContainerController;
+ // TODO: remove after unification
+ Task mTask;
/**
* Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
@@ -419,40 +441,54 @@
mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
- TaskWindowContainerController getWindowContainerController() {
- return mWindowContainerController;
+ Task getTask() {
+ return mTask;
}
- void createWindowContainer(boolean onTop, boolean showForAllUsers) {
- if (mWindowContainerController != null) {
- throw new IllegalArgumentException("Window container=" + mWindowContainerController
+ void createTask(boolean onTop, boolean showForAllUsers) {
+ if (mTask != null) {
+ throw new IllegalArgumentException("mTask=" + mTask
+ " already created for task=" + this);
}
final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
- setWindowContainerController(new TaskWindowContainerController(taskId, this,
- getStack().getWindowContainerController(), userId, bounds,
- mResizeMode, mSupportsPictureInPicture, onTop,
- showForAllUsers, lastTaskDescription));
- }
+ final StackWindowController stackController = getStack().getWindowContainerController();
- /**
- * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
- */
- @VisibleForTesting
- protected void setWindowContainerController(TaskWindowContainerController controller) {
- if (mWindowContainerController != null) {
- throw new IllegalArgumentException("Window container=" + mWindowContainerController
- + " already created for task=" + this);
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "TaskRecord: taskId=" + taskId
+ + " stack=" + stackController + " bounds=" + bounds);
}
- mWindowContainerController = controller;
+ final TaskStack stack = stackController.mContainer;
+ if (stack == null) {
+ throw new IllegalArgumentException("TaskRecord: invalid stack="
+ + stackController);
+ }
+ EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
+ mTask = new Task(taskId, stack, userId, mService.mWindowManager, mResizeMode,
+ mSupportsPictureInPicture, lastTaskDescription, this);
+ final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
+
+ if (!mDisplayedBounds.isEmpty()) {
+ mTask.setOverrideDisplayedBounds(mDisplayedBounds);
+ }
+ // We only want to move the parents to the parents if we are creating this task at the
+ // top of its stack.
+ stack.addTask(mTask, position, showForAllUsers, onTop /* moveParents */);
+ }
+
+ void setTask(Task task) {
+ mTask = task;
}
void removeWindowContainer() {
mService.getLockTaskController().clearLockedTask(this);
- mWindowContainerController.removeContainer();
- mWindowContainerController = null;
+ if (mTask == null) {
+ if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + taskId);
+ return;
+ }
+ mTask.removeIfPossible();
+ mTask = null;
if (!getWindowConfiguration().persistTaskBounds()) {
// Reset current bounds for task whose bounds shouldn't be persisted so it uses
// default configuration the next time it launches.
@@ -461,7 +497,6 @@
mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
}
- @Override
public void onSnapshotChanged(TaskSnapshot snapshot) {
mService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(taskId, snapshot);
}
@@ -471,17 +506,20 @@
return;
}
mResizeMode = resizeMode;
- mWindowContainerController.setResizeable(resizeMode);
+ mTask.setResizeable(resizeMode);
mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
void setTaskDockedResizing(boolean resizing) {
- mWindowContainerController.setTaskDockedResizing(resizing);
+ if (mTask == null) {
+ Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + taskId + " not found.");
+ return;
+ }
+ mTask.setTaskDockedResizing(resizing);
}
// TODO: Consolidate this with the resize() method below.
- @Override
public void requestResize(Rect bounds, int resizeMode) {
mService.resizeTask(taskId, bounds, resizeMode);
}
@@ -498,12 +536,12 @@
// If this is a forced resize, let it go through even if the bounds is not changing,
// as we might need a relayout due to surface size change (to/from fullscreen).
final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
- if (equivalentOverrideBounds(bounds) && !forced) {
+ if (equivalentRequestedOverrideBounds(bounds) && !forced) {
// Nothing to do here...
return true;
}
- if (mWindowContainerController == null) {
+ if (mTask == null) {
// Task doesn't exist in window manager yet (e.g. was restored from recents).
// All we can do for now is update the bounds so it can be used when the task is
// added to window manager.
@@ -550,7 +588,7 @@
}
}
}
- mWindowContainerController.resize(kept, forced);
+ mTask.resize(kept, forced);
saveLaunchingStateIfNeeded();
@@ -563,11 +601,15 @@
// TODO: Investigate combining with the resize() method above.
void resizeWindowContainer() {
- mWindowContainerController.resize(false /* relayout */, false /* forced */);
+ mTask.resize(false /* relayout */, false /* forced */);
}
void getWindowContainerBounds(Rect bounds) {
- mWindowContainerController.getBounds(bounds);
+ if (mTask != null) {
+ mTask.getBounds(bounds);
+ } else {
+ bounds.setEmpty();
+ }
}
/**
@@ -671,7 +713,7 @@
// Must reparent first in window manager to avoid a situation where AM can delete the
// we are coming from in WM before we reparent because it became empty.
- mWindowContainerController.reparent(toStack.getWindowContainerController(), position,
+ mTask.reparent(toStack.getWindowContainerController(), position,
moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
@@ -712,12 +754,12 @@
// Make sure the task has the appropriate bounds/size for the stack it is in.
final boolean toStackSplitScreenPrimary =
toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- final Rect configBounds = getOverrideBounds();
+ final Rect configBounds = getRequestedOverrideBounds();
if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN
|| toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
- && !Objects.equals(configBounds, toStack.getOverrideBounds())) {
- kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
- deferResume);
+ && !Objects.equals(configBounds, toStack.getRequestedOverrideBounds())) {
+ kept = resize(toStack.getRequestedOverrideBounds(), RESIZE_MODE_SYSTEM,
+ !mightReplaceWindow, deferResume);
} else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
Rect bounds = getLaunchBounds();
if (bounds == null) {
@@ -731,8 +773,8 @@
// mode
mService.mStackSupervisor.moveRecentsStackToFront(reason);
}
- kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
- deferResume);
+ kept = resize(toStack.getRequestedOverrideBounds(), RESIZE_MODE_SYSTEM,
+ !mightReplaceWindow, deferResume);
}
} finally {
windowManager.continueSurfaceLayout();
@@ -771,7 +813,11 @@
}
void cancelWindowTransition() {
- mWindowContainerController.cancelWindowTransition();
+ if (mTask == null) {
+ Slog.w(TAG_WM, "cancelWindowTransition: taskId " + taskId + " not found.");
+ return;
+ }
+ mTask.cancelTaskWindowTransition();
}
/**
@@ -1182,7 +1228,7 @@
mActivities.add(newTop);
// Make sure window manager is aware of the position change.
- mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
+ mTask.positionChildAtTop(newTop.mAppWindowToken);
updateEffectiveIntent();
setFrontOfTask();
@@ -1267,7 +1313,7 @@
if (r.mAppWindowToken != null) {
// Only attempt to move in WM if the child has a controller. It is possible we haven't
// created controller for the activity we are starting yet.
- mWindowContainerController.positionChildAt(r.mAppWindowToken, index);
+ mTask.positionChildAt(r.mAppWindowToken, index);
}
// Make sure the list of display UID whitelists is updated
@@ -1538,7 +1584,7 @@
return true;
}
final boolean landscape = bounds.width() > bounds.height();
- final Rect configBounds = getOverrideBounds();
+ final Rect configBounds = getRequestedOverrideBounds();
if (mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION) {
return configBounds.isEmpty()
|| landscape == (configBounds.width() > configBounds.height());
@@ -1635,8 +1681,8 @@
}
lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
colorPrimary, colorBackground, statusBarColor, navigationBarColor);
- if (mWindowContainerController != null) {
- mWindowContainerController.setTaskDescription(lastTaskDescription);
+ if (mTask != null) {
+ mTask.setTaskDescription(lastTaskDescription);
}
// Update the task affiliation color if we are the parent of the group
if (taskId == mAffiliatedTaskId) {
@@ -1679,7 +1725,7 @@
// If the task has no requested minimal size, we'd like to enforce a minimal size
// so that the user can not render the task too small to manipulate. We don't need
// to do this for the pinned stack as the bounds are controlled by the system.
- if (!inPinnedWindowingMode()) {
+ if (!inPinnedWindowingMode() && mStack != null) {
final int defaultMinSizeDp =
mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
final ActivityDisplay display =
@@ -1701,7 +1747,7 @@
return;
}
- final Rect configBounds = getOverrideBounds();
+ final Rect configBounds = getRequestedOverrideBounds();
if (adjustWidth) {
if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
bounds.left = bounds.right - minWidth;
@@ -1723,25 +1769,6 @@
}
/**
- * @return a new Configuration for this Task, given the provided {@param bounds} and
- * {@param insetBounds}.
- */
- Configuration computeNewOverrideConfigurationForBounds(Rect bounds, Rect insetBounds) {
- // Compute a new override configuration for the given bounds, if fullscreen bounds
- // (bounds == null), then leave the override config unset
- final Configuration newOverrideConfig = new Configuration();
- if (bounds != null) {
- newOverrideConfig.setTo(getOverrideConfiguration());
- mTmpRect.set(bounds);
- adjustForMinimalTaskDimensions(mTmpRect);
- computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
- mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
- }
-
- return newOverrideConfig;
- }
-
- /**
* Update task's override configuration based on the bounds.
* @param bounds The bounds of the task.
* @return True if the override configuration was updated.
@@ -1767,35 +1794,21 @@
* @return True if the override configuration was updated.
*/
boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
- if (equivalentOverrideBounds(bounds)) {
+ final boolean hasSetDisplayedBounds = (insetBounds != null && !insetBounds.isEmpty());
+ if (hasSetDisplayedBounds) {
+ setDisplayedBounds(bounds);
+ } else {
+ setDisplayedBounds(null);
+ }
+ // "steady" bounds do not include any temporary offsets from animation or interaction.
+ Rect steadyBounds = hasSetDisplayedBounds ? insetBounds : bounds;
+ if (equivalentRequestedOverrideBounds(steadyBounds)) {
return false;
}
- final Rect currentBounds = getOverrideBounds();
- mTmpConfig.setTo(getOverrideConfiguration());
- final Configuration newConfig = getOverrideConfiguration();
-
- final boolean matchParentBounds = bounds == null || bounds.isEmpty();
- final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
- if (matchParentBounds) {
- if (!currentBounds.isEmpty() && persistBounds) {
- setLastNonFullscreenBounds(currentBounds);
- }
- setBounds(null);
- newConfig.unset();
- } else {
- mTmpRect.set(bounds);
- adjustForMinimalTaskDimensions(mTmpRect);
- setBounds(mTmpRect);
-
- if (mStack == null || persistBounds) {
- setLastNonFullscreenBounds(getOverrideBounds());
- }
- computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
- mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
- }
- onOverrideConfigurationChanged(newConfig);
- return !mTmpConfig.equals(newConfig);
+ mTmpConfig.setTo(getResolvedOverrideConfiguration());
+ setBounds(steadyBounds);
+ return !mTmpConfig.equals(getResolvedOverrideConfiguration());
}
/**
@@ -1821,6 +1834,12 @@
if (wasInMultiWindowMode != inMultiWindowMode()) {
mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
}
+ if (getWindowConfiguration().persistTaskBounds()) {
+ final Rect currentBounds = getRequestedOverrideBounds();
+ if (!currentBounds.isEmpty()) {
+ setLastNonFullscreenBounds(currentBounds);
+ }
+ }
// TODO: Should also take care of Pip mode changes here.
saveLaunchingStateIfNeeded();
@@ -1847,46 +1866,276 @@
mService.mStackSupervisor.mLaunchParamsPersister.saveTask(this);
}
- /** Clears passed config and fills it with new override values. */
- // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
- // depend on task or stacks, but uses those object to get the display to base the calculation
- // on. Probably best to centralize calculations like this in ConfigurationContainer.
- void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
- boolean overrideWidth, boolean overrideHeight) {
- mTmpNonDecorBounds.set(bounds);
- mTmpStableBounds.set(bounds);
-
- config.unset();
- final Configuration parentConfig = getParent().getConfiguration();
-
- final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
-
- if (mStack != null) {
- final StackWindowController stackController = mStack.getWindowContainerController();
- stackController.adjustConfigurationForBounds(bounds, insetBounds,
- mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
- config, parentConfig, getWindowingMode());
- } else {
- throw new IllegalArgumentException("Expected stack when calculating override config");
+ /**
+ * Adjust bounds to stay within stack bounds.
+ *
+ * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
+ * that keep them unchanged, but be contained within the stack bounds.
+ *
+ * @param bounds Bounds to be adjusted.
+ * @param stackBounds Bounds within which the other bounds should remain.
+ */
+ private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
+ if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) {
+ return;
}
- config.orientation = (config.screenWidthDp <= config.screenHeightDp)
- ? Configuration.ORIENTATION_PORTRAIT
- : Configuration.ORIENTATION_LANDSCAPE;
+ if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
+ final int maxRight = stackBounds.right
+ - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
+ int horizontalDiff = stackBounds.left - bounds.left;
+ if ((horizontalDiff < 0 && bounds.left >= maxRight)
+ || (bounds.left + horizontalDiff >= maxRight)) {
+ horizontalDiff = maxRight - bounds.left;
+ }
+ bounds.left += horizontalDiff;
+ bounds.right += horizontalDiff;
+ }
- // For calculating screen layout, we need to use the non-decor inset screen area for the
- // calculation for compatibility reasons, i.e. screen area without system bars that could
- // never go away in Honeycomb.
- final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
- final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
- // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
- // calculation with partial default.
- // Reducing the screen layout starting from its parent config.
- final int sl = parentConfig.screenLayout &
- (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
- final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
- final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
- config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
+ if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
+ final int maxBottom = stackBounds.bottom
+ - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
+ int verticalDiff = stackBounds.top - bounds.top;
+ if ((verticalDiff < 0 && bounds.top >= maxBottom)
+ || (bounds.top + verticalDiff >= maxBottom)) {
+ verticalDiff = maxBottom - bounds.top;
+ }
+ bounds.top += verticalDiff;
+ bounds.bottom += verticalDiff;
+ }
+ }
+
+ /**
+ * Displayed bounds are used to set where the task is drawn at any given time. This is
+ * separate from its actual bounds so that the app doesn't see any meaningful configuration
+ * changes during transitionary periods.
+ */
+ void setDisplayedBounds(Rect bounds) {
+ if (bounds == null) {
+ mDisplayedBounds.setEmpty();
+ } else {
+ mDisplayedBounds.set(bounds);
+ }
+ if (mTask != null) {
+ mTask.setOverrideDisplayedBounds(
+ mDisplayedBounds.isEmpty() ? null : mDisplayedBounds);
+ }
+ }
+
+ /**
+ * Gets the current overridden displayed bounds. These will be empty if the task is not
+ * currently overriding where it is displayed.
+ */
+ Rect getDisplayedBounds() {
+ return mDisplayedBounds;
+ }
+
+ /**
+ * @return {@code true} if this has overridden displayed bounds.
+ */
+ boolean hasDisplayedBounds() {
+ return !mDisplayedBounds.isEmpty();
+ }
+
+ /**
+ * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
+ * intersectBounds on a side, then the respective side will not be intersected.
+ *
+ * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
+ * inset on that side is no-longer applicable. This scenario happens when a task's minimal
+ * bounds are larger than the provided parent/display bounds.
+ *
+ * @param inOutBounds the bounds to intersect.
+ * @param intersectBounds the bounds to intersect with.
+ * @param intersectInsets insets to apply to intersectBounds before intersecting.
+ */
+ private static void intersectWithInsetsIfFits(
+ Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
+ if (inOutBounds.right <= intersectBounds.right) {
+ inOutBounds.right =
+ Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
+ }
+ if (inOutBounds.bottom <= intersectBounds.bottom) {
+ inOutBounds.bottom =
+ Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
+ }
+ if (inOutBounds.left >= intersectBounds.left) {
+ inOutBounds.left =
+ Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
+ }
+ if (inOutBounds.top >= intersectBounds.top) {
+ inOutBounds.top =
+ Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
+ }
+ }
+
+ /**
+ * Gets bounds with non-decor and stable insets applied respectively.
+ *
+ * If bounds overhangs the display, those edges will not get insets. See
+ * {@link #intersectWithInsetsIfFits}
+ *
+ * @param outNonDecorBounds where to place bounds with non-decor insets applied.
+ * @param outStableBounds where to place bounds with stable insets applied.
+ * @param bounds the bounds to inset.
+ */
+ private void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
+ DisplayInfo displayInfo) {
+ outNonDecorBounds.set(bounds);
+ outStableBounds.set(bounds);
+ if (getStack() == null || getStack().getDisplay() == null) {
+ return;
+ }
+ DisplayPolicy policy = getStack().getDisplay().mDisplayContent.getDisplayPolicy();
+ if (policy == null) {
+ return;
+ }
+ mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ policy.getStableInsetsLw(displayInfo.rotation,
+ displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout,
+ mTmpInsets);
+ intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
+
+ policy.getNonDecorInsetsLw(displayInfo.rotation,
+ displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout,
+ mTmpInsets);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
+ }
+
+ /**
+ * Asks docked-divider controller for the smallestwidthdp given bounds.
+ * @param bounds bounds to calculate smallestwidthdp for.
+ */
+ private int getSmallestScreenWidthDpForDockedBounds(Rect bounds) {
+ DisplayContent dc = mStack.getDisplay().mDisplayContent;
+ if (dc != null) {
+ return dc.getDockedDividerController().getSmallestWidthDpForBounds(bounds);
+ }
+ return Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+ }
+
+ /**
+ * Calculates configuration values used by the client to get resources. This should be run
+ * using app-facing bounds (bounds unmodified by animations or transient interactions).
+ *
+ * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
+ * configuring an "inherit-bounds" window which means that all configuration settings would
+ * just be inherited from the parent configuration.
+ **/
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Rect bounds,
+ @NonNull Configuration parentConfig) {
+ int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ }
+
+ float density = inOutConfig.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = parentConfig.densityDpi;
+ }
+ density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+
+ Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (outAppBounds == null || outAppBounds.isEmpty()) {
+ inOutConfig.windowConfiguration.setAppBounds(bounds);
+ outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ }
+ if (windowingMode != WINDOWING_MODE_FREEFORM) {
+ final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
+ if (parentAppBounds != null && !parentAppBounds.isEmpty()) {
+ outAppBounds.intersect(parentAppBounds);
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
+ || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ if (mStack != null) {
+ final DisplayInfo di = new DisplayInfo();
+ mStack.getDisplay().mDisplay.getDisplayInfo(di);
+
+ // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
+ // area, i.e. the screen area without the system bars.
+ // The non decor inset are areas that could never be removed in Honeycomb. See
+ // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
+ calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, bounds, di);
+ } else {
+ mTmpNonDecorBounds.set(bounds);
+ mTmpStableBounds.set(bounds);
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ inOutConfig.screenWidthDp = Math.min((int) (mTmpStableBounds.width() / density),
+ parentConfig.screenWidthDp);
+ }
+ if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ inOutConfig.screenHeightDp = Math.min((int) (mTmpStableBounds.height() / density),
+ parentConfig.screenHeightDp);
+ }
+
+ if (inOutConfig.smallestScreenWidthDp
+ == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ if (WindowConfiguration.isFloating(windowingMode)) {
+ // For floating tasks, calculate the smallest width from the bounds of the task
+ inOutConfig.smallestScreenWidthDp = (int) (
+ Math.min(bounds.width(), bounds.height()) / density);
+ } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ // Iterating across all screen orientations, and return the minimum of the task
+ // width taking into account that the bounds might change because the snap
+ // algorithm snaps to a different value
+ getSmallestScreenWidthDpForDockedBounds(bounds);
+ }
+ // otherwise, it will just inherit
+ }
+ }
+
+ if (inOutConfig.orientation == Configuration.ORIENTATION_UNDEFINED) {
+ inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+ ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
+ }
+ if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+ // For calculating screen layout, we need to use the non-decor inset screen area for the
+ // calculation for compatibility reasons, i.e. screen area without system bars that
+ // could never go away in Honeycomb.
+ final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+ final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start
+ // override calculation with partial default.
+ // Reducing the screen layout starting from its parent config.
+ final int sl = parentConfig.screenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+ final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
+ final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
+ inOutConfig.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
+ }
+ }
+
+ // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
+ void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
+ Configuration overrideConfig) {
+ inOutConfig.setTo(overrideConfig);
+
+ Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
+ if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
+ adjustForMinimalTaskDimensions(outOverrideBounds);
+
+ int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ // by policy, make sure the window remains within parent
+ fitWithinBounds(outOverrideBounds, parentConfig.windowConfiguration.getBounds());
+ }
+
+ computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig);
+ }
+ }
+
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig,
+ getRequestedOverrideConfiguration());
}
Rect updateOverrideConfigurationFromLaunchBounds() {
@@ -1895,7 +2144,7 @@
if (bounds != null && !bounds.isEmpty()) {
// TODO: Review if we actually want to do this - we are setting the launch bounds
// directly here.
- bounds.set(getOverrideBounds());
+ bounds.set(getRequestedOverrideBounds());
}
return bounds;
}
@@ -1921,7 +2170,7 @@
mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
}
} else {
- updateOverrideConfiguration(inStack.getOverrideBounds());
+ updateOverrideConfiguration(inStack.getRequestedOverrideBounds());
}
}
@@ -1935,9 +2184,9 @@
if (!isActivityTypeStandardOrUndefined()
|| windowingMode == WINDOWING_MODE_FULLSCREEN
|| (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
- return isResizeable() ? mStack.getOverrideBounds() : null;
+ return isResizeable() ? mStack.getRequestedOverrideBounds() : null;
} else if (!getWindowConfiguration().persistTaskBounds()) {
- return mStack.getOverrideBounds();
+ return mStack.getRequestedOverrideBounds();
}
return mLastNonFullscreenBounds;
}
@@ -2156,7 +2405,7 @@
proto.write(FULLSCREEN, matchParentBounds());
if (!matchParentBounds()) {
- final Rect bounds = getOverrideBounds();
+ final Rect bounds = getRequestedOverrideBounds();
bounds.writeToProto(proto, BOUNDS);
}
proto.write(MIN_WIDTH, mMinWidth);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 7ab4d08..01a5622 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -191,9 +191,7 @@
} else {
mCache.putSnapshot(task, snapshot);
mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- if (task.getController() != null) {
- task.getController().reportSnapshotChanged(snapshot);
- }
+ task.onSnapshotChanged(snapshot);
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b1f95f8..11d9ebb 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -184,9 +184,15 @@
// Update bounds of containing tasks.
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = mChildren.get(taskNdx);
- task.setBounds(taskBounds.get(task.mTaskId), false /* forced */);
- task.setTempInsetBounds(taskTempInsetBounds != null ?
- taskTempInsetBounds.get(task.mTaskId) : null);
+ final Rect insetBounds =
+ taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) : null;
+ if (insetBounds != null) {
+ task.setBounds(insetBounds);
+ task.setOverrideDisplayedBounds(taskBounds.get(task.mTaskId));
+ } else {
+ task.setBounds(taskBounds.get(task.mTaskId));
+ task.setOverrideDisplayedBounds(null);
+ }
}
return true;
}
@@ -276,7 +282,7 @@
@Override
public int setBounds(Rect bounds) {
- return setBounds(getOverrideBounds(), bounds);
+ return setBounds(getRequestedOverrideBounds(), bounds);
}
private int setBounds(Rect existing, Rect bounds) {
@@ -298,8 +304,8 @@
/** Bounds of the stack without adjusting for other factors in the system like visibility
* of docked stack.
- * Most callers should be using {@link ConfigurationContainer#getOverrideBounds} as it take into
- * consideration other system factors. */
+ * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a
+ * it takes into consideration other system factors. */
void getRawBounds(Rect out) {
out.set(getRawBounds());
}
@@ -442,7 +448,8 @@
* @param inOutBounds the bounds to update (both input and output).
*/
void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) {
- final boolean primary = getOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ final boolean primary =
+ getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds);
final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout;
snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds);
@@ -739,7 +746,7 @@
return;
}
- final Rect stackBounds = getBounds();
+ final Rect stackBounds = getDisplayedBounds();
int width = stackBounds.width();
int height = stackBounds.height();
@@ -1754,14 +1761,6 @@
scheduleAnimation();
}
- @Override
- void getRelativePosition(Point outPos) {
- super.getRelativePosition(outPos);
- final int outset = getStackOutset();
- outPos.x -= outset;
- outPos.y -= outset;
- }
-
AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
return mAnimatingAppWindowTokenRegistry;
}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
deleted file mode 100644
index ec64d2e..0000000
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static com.android.server.EventLogTags.WM_TASK_CREATED;
-import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityManager.TaskSnapshot;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.EventLog;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Controller for the task container. This is created by activity manager to link task records to
- * the task container they use in window manager.
- *
- * Test class: {@link TaskWindowContainerControllerTests}
- */
-public class TaskWindowContainerController
- extends WindowContainerController<Task, TaskWindowContainerListener> {
-
- private final int mTaskId;
- private final H mHandler;
-
- public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
- StackWindowController stackController, int userId, Rect bounds, int resizeMode,
- boolean supportsPictureInPicture, boolean toTop, boolean showForAllUsers,
- TaskDescription taskDescription) {
- this(taskId, listener, stackController, userId, bounds, resizeMode,
- supportsPictureInPicture, toTop, showForAllUsers, taskDescription,
- WindowManagerService.getInstance());
- }
-
- public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
- StackWindowController stackController, int userId, Rect bounds, int resizeMode,
- boolean supportsPictureInPicture, boolean toTop, boolean showForAllUsers,
- TaskDescription taskDescription, WindowManagerService service) {
- super(listener, service);
- mTaskId = taskId;
- mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
-
- synchronized (mGlobalLock) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "TaskWindowContainerController: taskId=" + taskId
- + " stack=" + stackController + " bounds=" + bounds);
-
- final TaskStack stack = stackController.mContainer;
- if (stack == null) {
- throw new IllegalArgumentException("TaskWindowContainerController: invalid stack="
- + stackController);
- }
- EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
- final Task task = createTask(taskId, stack, userId, resizeMode,
- supportsPictureInPicture, taskDescription);
- final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
- // We only want to move the parents to the parents if we are creating this task at the
- // top of its stack.
- stack.addTask(task, position, showForAllUsers, toTop /* moveParents */);
- }
- }
-
- @VisibleForTesting
- Task createTask(int taskId, TaskStack stack, int userId, int resizeMode,
- boolean supportsPictureInPicture, TaskDescription taskDescription) {
- return new Task(taskId, stack, userId, mService, resizeMode, supportsPictureInPicture,
- taskDescription, this);
- }
-
- @Override
- public void removeContainer() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + mTaskId);
- return;
- }
- mContainer.removeIfPossible();
- super.removeContainer();
- }
- }
-
- void positionChildAtTop(AppWindowToken aToken) {
- positionChildAt(aToken, POSITION_TOP);
- }
-
- void positionChildAt(AppWindowToken aToken, int position) {
- synchronized (mService.mGlobalLock) {
- if (aToken == null) {
- Slog.w(TAG_WM,
- "Attempted to position of non-existing app");
- return;
- }
-
- final Task task = mContainer;
- if (task == null) {
- throw new IllegalArgumentException("positionChildAt: invalid task=" + this);
- }
- task.positionChildAt(position, aToken, false /* includeParents */);
- }
- }
-
- public void reparent(StackWindowController stackController, int position, boolean moveParents) {
- synchronized (mGlobalLock) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
- + " to stack=" + stackController + " at " + position);
- if (mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "reparent: could not find taskId=" + mTaskId);
- return;
- }
- final TaskStack stack = stackController.mContainer;
- if (stack == null) {
- throw new IllegalArgumentException("reparent: could not find stack="
- + stackController);
- }
- mContainer.reparent(stack, position, moveParents);
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
- }
-
- public void setResizeable(int resizeMode) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.setResizeable(resizeMode);
- }
- }
- }
-
- public void resize(boolean relayout, boolean forced) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("resizeTask: taskId " + mTaskId + " not found.");
- }
-
- if (mContainer.setBounds(mContainer.getOverrideBounds(), forced) != BOUNDS_CHANGE_NONE
- && relayout) {
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
- }
- }
-
- public void getBounds(Rect bounds) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getBounds(bounds);
- return;
- }
- bounds.setEmpty();
- }
- }
-
- /**
- * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
- *
- * @param resizing Whether to put the task into drag resize mode.
- */
- public void setTaskDockedResizing(boolean resizing) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + mTaskId + " not found.");
- return;
- }
- mContainer.setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }
- }
-
- public void cancelWindowTransition() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "cancelWindowTransition: taskId " + mTaskId + " not found.");
- return;
- }
- mContainer.cancelTaskWindowTransition();
- }
- }
-
- public void setTaskDescription(TaskDescription taskDescription) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "setTaskDescription: taskId " + mTaskId + " not found.");
- return;
- }
- mContainer.setTaskDescription(taskDescription);
- }
- }
-
- public boolean isDragResizing() {
- synchronized (mGlobalLock) {
- return mContainer.isDragResizing();
- }
- }
-
- void reportSnapshotChanged(TaskSnapshot snapshot) {
- mHandler.obtainMessage(H.REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
- }
-
- void requestResize(Rect bounds, int resizeMode) {
- mHandler.obtainMessage(H.REQUEST_RESIZE, resizeMode, 0, bounds).sendToTarget();
- }
-
- @Override
- public String toString() {
- return "{TaskWindowContainerController taskId=" + mTaskId + "}";
- }
-
- private static final class H extends Handler {
-
- static final int REPORT_SNAPSHOT_CHANGED = 0;
- static final int REQUEST_RESIZE = 1;
-
- private final WeakReference<TaskWindowContainerController> mController;
-
- H(WeakReference<TaskWindowContainerController> controller, Looper looper) {
- super(looper);
- mController = controller;
- }
-
- @Override
- public void handleMessage(Message msg) {
- final TaskWindowContainerController controller = mController.get();
- final TaskWindowContainerListener listener = (controller != null)
- ? controller.mListener : null;
- if (listener == null) {
- return;
- }
- switch (msg.what) {
- case REPORT_SNAPSHOT_CHANGED:
- listener.onSnapshotChanged((TaskSnapshot) msg.obj);
- break;
- case REQUEST_RESIZE:
- listener.requestResize((Rect) msg.obj, msg.arg1);
- break;
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
deleted file mode 100644
index af67de3..0000000
--- a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.graphics.Rect;
-
-/**
- * Interface used by the creator of {@link TaskWindowContainerController} to listen to changes with
- * the task container.
- */
-public interface TaskWindowContainerListener extends WindowContainerListener {
-
- /** Called when the snapshot of this task has changed. */
- void onSnapshotChanged(TaskSnapshot snapshot);
-
- /** Called when the task container would like its controller to resize. */
- void requestResize(Rect bounds, int resizeMode);
-}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1275ca0..651089d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -469,15 +469,16 @@
/**
* Update override configuration and recalculate full config.
- * @see #mOverrideConfiguration
+ * @see #mRequestedOverrideConfiguration
* @see #mFullConfiguration
*/
@Override
- public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
// We must diff before the configuration is applied so that we can capture the change
// against the existing bounds.
- final int diff = diffOverrideBounds(overrideConfiguration.windowConfiguration.getBounds());
- super.onOverrideConfigurationChanged(overrideConfiguration);
+ final int diff = diffRequestedOverrideBounds(
+ overrideConfiguration.windowConfiguration.getBounds());
+ super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
if (mParent != null) {
mParent.onDescendantOverrideConfigurationChanged();
}
@@ -1266,7 +1267,7 @@
return;
}
- getRelativePosition(mTmpPos);
+ getRelativeDisplayedPosition(mTmpPos);
if (mTmpPos.equals(mLastSurfacePosition)) {
return;
}
@@ -1275,12 +1276,22 @@
mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
}
- void getRelativePosition(Point outPos) {
- final Rect bounds = getBounds();
- outPos.set(bounds.left, bounds.top);
+ /**
+ * Displayed bounds specify where to display this container at. It differs from bounds during
+ * certain operations (like animation or interactive dragging).
+ *
+ * @return the bounds to display this container at.
+ */
+ Rect getDisplayedBounds() {
+ return getBounds();
+ }
+
+ void getRelativeDisplayedPosition(Point outPos) {
+ final Rect dispBounds = getDisplayedBounds();
+ outPos.set(dispBounds.left, dispBounds.top);
final WindowContainer parent = getParent();
if (parent != null) {
- final Rect parentBounds = parent.getBounds();
+ final Rect parentBounds = parent.getDisplayedBounds();
outPos.offset(-parentBounds.left, -parentBounds.top);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainerController.java b/services/core/java/com/android/server/wm/WindowContainerController.java
index 7cb89c5..17bc0e2 100644
--- a/services/core/java/com/android/server/wm/WindowContainerController.java
+++ b/services/core/java/com/android/server/wm/WindowContainerController.java
@@ -72,12 +72,12 @@
}
@Override
- public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
synchronized (mGlobalLock) {
if (mContainer == null) {
return;
}
- mContainer.onOverrideConfigurationChanged(overrideConfiguration);
+ mContainer.onRequestedOverrideConfigurationChanged(overrideConfiguration);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index e83b863..9f1a587 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipData;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
@@ -449,11 +448,4 @@
* Return the display Id for given window.
*/
public abstract int getDisplayIdForWindow(IBinder windowToken);
-
- // TODO: use WindowProcessController once go/wm-unified is done.
- /**
- * Notifies the window manager that configuration of the process associated with the input pid
- * changed.
- */
- public abstract void onProcessConfigurationChanged(int pid, Configuration newConfig);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 52b24b3..002d6d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -735,6 +735,7 @@
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
+ final ActivityTaskManagerService mAtmService;
// Indicates whether this device supports wide color gamut / HDR rendering
private boolean mHasWideColorGamutSupport;
@@ -897,11 +898,10 @@
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
- final WindowManagerGlobalLock globalLock) {
+ ActivityTaskManagerService atm) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
- globalLock),
- 0);
+ atm), 0);
return sInstance;
}
@@ -923,9 +923,10 @@
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
- WindowManagerGlobalLock globalLock) {
+ ActivityTaskManagerService atm) {
installLock(this, INDEX_WINDOW);
- mGlobalLock = globalLock;
+ mGlobalLock = atm.getGlobalLock();
+ mAtmService = atm;
mContext = context;
mAllowBootMessages = showBootMsgs;
mOnlyCore = onlyCore;
@@ -7281,19 +7282,6 @@
return Display.INVALID_DISPLAY;
}
}
-
- @Override
- public void onProcessConfigurationChanged(int pid, Configuration newConfig) {
- synchronized (mGlobalLock) {
- Configuration currentConfig = mProcessConfigurations.get(pid);
- if (currentConfig == null) {
- currentConfig = new Configuration(newConfig);
- } else {
- currentConfig.setTo(newConfig);
- }
- mProcessConfigurations.put(pid, currentConfig);
- }
- }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2dec4dd..f7f7528 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -770,8 +770,8 @@
}
@Override
- public void onOverrideConfigurationChanged(Configuration newOverrideConfig) {
- super.onOverrideConfigurationChanged(newOverrideConfig);
+ public void onRequestedOverrideConfigurationChanged(Configuration newOverrideConfig) {
+ super.onRequestedOverrideConfigurationChanged(newOverrideConfig);
updateConfiguration();
}
@@ -924,7 +924,7 @@
}
}
pw.println(prefix + " Configuration=" + getConfiguration());
- pw.println(prefix + " OverrideConfiguration=" + getOverrideConfiguration());
+ pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration());
pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ef22bb8..d2dfa76 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -790,6 +790,18 @@
}
@Override
+ public Rect getDisplayedBounds() {
+ final Task task = getTask();
+ if (task != null) {
+ Rect bounds = task.getOverrideDisplayedBounds();
+ if (!bounds.isEmpty()) {
+ return bounds;
+ }
+ }
+ return super.getDisplayedBounds();
+ }
+
+ @Override
public void computeFrameLw() {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
@@ -805,16 +817,7 @@
final boolean windowsAreFloating = task != null && task.isFloating();
final DisplayContent dc = getDisplayContent();
- // If the task has temp inset bounds set, we have to make sure all its windows uses
- // the temp inset frame. Otherwise different display frames get applied to the main
- // window and the child window, making them misaligned.
- // Otherwise we need to clear the inset frame, to avoid using a stale frame after leaving
- // multi window mode.
- if (task != null && isInMultiWindowMode()) {
- task.getTempInsetBounds(mInsetFrame);
- } else {
- mInsetFrame.setEmpty();
- }
+ mInsetFrame.set(getBounds());
// Denotes the actual frame used to calculate the insets and to perform the layout. When
// resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
@@ -834,7 +837,7 @@
layoutXDiff = 0;
layoutYDiff = 0;
} else {
- getBounds(mWindowFrames.mContainingFrame);
+ mWindowFrames.mContainingFrame.set(getDisplayedBounds());
if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
@@ -884,14 +887,9 @@
layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
- layoutXDiff =
- !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
- : 0;
- layoutYDiff =
- !mInsetFrame.isEmpty() ? mInsetFrame.top - mWindowFrames.mContainingFrame.top
- : 0;
- layoutContainingFrame =
- !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
+ layoutXDiff = mInsetFrame.left - mWindowFrames.mContainingFrame.left;
+ layoutYDiff = mInsetFrame.top - mWindowFrames.mContainingFrame.top;
+ layoutContainingFrame = mInsetFrame;
mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
mTmpRect);
@@ -2145,7 +2143,7 @@
}
}
- int getTouchableRegion(Region region, int flags) {
+ int getSurfaceTouchableRegion(Region region, int flags) {
final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
if (modal && mAppToken != null) {
// Limit the outer touch to the activity stack region.
@@ -2173,12 +2171,9 @@
region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
} else {
// Not modal or full screen modal
- getTouchableRegion(region);
+ getTouchableRegion(region, true /* forSurface */);
}
- // The area containing the shadows is not touchable.
- region.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
-
return flags;
}
@@ -2268,8 +2263,10 @@
// For child windows we want to use the pid for the parent window in case the the child
// window was added from another process.
final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid;
- mTempConfiguration.setTo(mWmService.mProcessConfigurations.get(
- pid, mWmService.mRoot.getConfiguration()));
+ final Configuration processConfig =
+ mWmService.mAtmService.getGlobalConfigurationForPid(pid);
+ mTempConfiguration.setTo(processConfig == null
+ ? mWmService.mRoot.getConfiguration() : processConfig);
return mTempConfiguration;
}
@@ -2810,7 +2807,13 @@
frame.right - inset.right, frame.bottom - inset.bottom);
}
+ /** Get the touchable region in global coordinates. */
void getTouchableRegion(Region outRegion) {
+ getTouchableRegion(outRegion, false /* forSurface */);
+ }
+
+ /** If {@param forSuface} is {@code true}, the region will be translated to surface based. */
+ private void getTouchableRegion(Region outRegion, boolean forSurface) {
if (inPinnedWindowingMode() && !isFocused()) {
outRegion.setEmpty();
return;
@@ -2821,22 +2824,27 @@
default:
case TOUCHABLE_INSETS_FRAME:
outRegion.set(frame);
- outRegion.translate(-frame.left, -frame.top);
break;
case TOUCHABLE_INSETS_CONTENT:
applyInsets(outRegion, frame, mGivenContentInsets);
- outRegion.translate(-frame.left, -frame.top);
break;
case TOUCHABLE_INSETS_VISIBLE:
applyInsets(outRegion, frame, mGivenVisibleInsets);
- outRegion.translate(-frame.left, -frame.top);
break;
case TOUCHABLE_INSETS_REGION: {
outRegion.set(mGivenTouchableRegion);
break;
}
}
- outRegion.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
+ cropRegionToStackBoundsIfNeeded(outRegion);
+
+ if (forSurface) {
+ if (mTouchableInsets != TOUCHABLE_INSETS_REGION) {
+ outRegion.translate(-frame.left, -frame.top);
+ }
+ outRegion.getBounds(mTmpRect);
+ applyInsets(outRegion, mTmpRect, mAttrs.surfaceInsets);
+ }
}
private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -4614,7 +4622,7 @@
outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
-parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
} else if (parentWindowContainer != null) {
- final Rect parentBounds = parentWindowContainer.getBounds();
+ final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 8bc6db7..fb5c556 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1020,7 +1020,7 @@
mTmpPos.x = 0;
mTmpPos.y = 0;
if (stack != null) {
- stack.getRelativePosition(mTmpPos);
+ stack.getRelativeDisplayedPosition(mTmpPos);
}
xOffset = -mTmpPos.x;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 8b873e3..6d72191 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,8 +2,6 @@
name: "libservices.core",
defaults: ["libservices.core-libs"],
- cpp_std: "c++17",
-
cflags: [
"-Wall",
"-Werror",
@@ -128,7 +126,6 @@
static_libs: [
"android.hardware.broadcastradio@common-utils-1x-lib",
- "libscrypt_static",
],
product_variables: {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 43d2dcf..bf83ca9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,6 +107,7 @@
jmethodID getLongPressTimeout;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
+ jmethodID getPointerDisplayId;
jmethodID getKeyboardLayoutOverlay;
jmethodID getDeviceAlias;
jmethodID getTouchCalibrationForInputDevice;
@@ -174,15 +175,6 @@
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
-static void updatePointerControllerFromViewport(
- sp<PointerController> controller, const DisplayViewport* const viewport) {
- if (controller != nullptr && viewport != nullptr) {
- const int32_t width = viewport->logicalRight - viewport->logicalLeft;
- const int32_t height = viewport->logicalBottom - viewport->logicalTop;
- controller->setDisplayViewport(width, height, viewport->orientation);
- }
-}
-
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -242,6 +234,7 @@
jfloatArray matrixArr);
virtual TouchAffineTransformation getTouchAffineTransformation(
const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
+ virtual void updatePointerDisplay();
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -314,10 +307,11 @@
std::atomic<bool> mInteractive;
- void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
+ void updateInactivityTimeoutLocked();
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
-
+ const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
+ int32_t getPointerDisplayId();
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
static inline JNIEnv* jniEnv() {
@@ -391,9 +385,10 @@
return false;
}
-static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
- for (const DisplayViewport& v : viewports) {
- if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
+ REQUIRES(mLock) {
+ for (const DisplayViewport& v : mLocked.viewports) {
+ if (v.displayId == displayId) {
return &v;
}
}
@@ -420,20 +415,10 @@
}
}
- const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
- {
+ { // acquire lock
AutoMutex _l(mLock);
- const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
- // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
- // if they are different.
- const bool internalViewportChanged = (newInternalViewport != nullptr) &&
- (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
- if (internalViewportChanged) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- updatePointerControllerFromViewport(controller, newInternalViewport);
- }
mLocked.viewports = viewports;
- }
+ } // release lock
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -556,15 +541,43 @@
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
-
- const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
- updatePointerControllerFromViewport(controller, internalViewport);
-
- updateInactivityTimeoutLocked(controller);
+ updateInactivityTimeoutLocked();
}
+
return controller;
}
+int32_t NativeInputManager::getPointerDisplayId() {
+ JNIEnv* env = jniEnv();
+ jint pointerDisplayId = env->CallIntMethod(mServiceObj,
+ gServiceClassInfo.getPointerDisplayId);
+ if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
+ pointerDisplayId = ADISPLAY_ID_DEFAULT;
+ }
+
+ return pointerDisplayId;
+}
+
+void NativeInputManager::updatePointerDisplay() {
+ ATRACE_CALL();
+
+ jint pointerDisplayId = getPointerDisplayId();
+
+ AutoMutex _l(mLock);
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != nullptr) {
+ const DisplayViewport* viewport = findDisplayViewportLocked(pointerDisplayId);
+ if (viewport == nullptr) {
+ ALOGW("Can't find pointer display viewport, fallback to default display.");
+ viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
+ }
+
+ if (viewport != nullptr) {
+ controller->setDisplayViewport(*viewport);
+ }
+ }
+}
+
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
@@ -821,16 +834,16 @@
if (mLocked.systemUiVisibility != visibility) {
mLocked.systemUiVisibility = visibility;
-
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- updateInactivityTimeoutLocked(controller);
- }
+ updateInactivityTimeoutLocked();
}
}
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
- REQUIRES(mLock) {
+void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller == nullptr) {
+ return;
+ }
+
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1824,6 +1837,9 @@
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
+ GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
+ "getPointerDisplayId", "()I");
+
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4d0556c..0e349b7 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -756,14 +756,18 @@
Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override;
Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override;
private:
- void translateGnssMeasurement_V1_0(
- const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
- JavaObject& object);
- jobjectArray translateGnssMeasurements(
- JNIEnv* env,
- const IGnssMeasurementCallback_V1_1::GnssMeasurement* measurements_v1_1,
- const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurements_v1_0,
- size_t count);
+ template<class T>
+ void translateSingleGnssMeasurement(const T* measurement, JavaObject& object);
+
+ template<class T>
+ jobjectArray translateAllGnssMeasurements(JNIEnv* env, const T* measurements, size_t count);
+
+ template<class T>
+ void translateAndSetGnssData(const T& data);
+
+ template<class T>
+ size_t getMeasurementCount(const T& data);
+
jobject translateGnssClock(
JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock);
void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray);
@@ -777,41 +781,48 @@
Return<void> GnssMeasurementCallback::gnssMeasurementCb(
const IGnssMeasurementCallback_V1_1::GnssData& data) {
- JNIEnv* env = getJniEnv();
-
- jobject clock;
- jobjectArray measurementArray;
-
- clock = translateGnssClock(env, &data.clock);
-
- measurementArray = translateGnssMeasurements(
- env, data.measurements.data(), nullptr, data.measurements.size());
- setMeasurementData(env, clock, measurementArray);
-
- env->DeleteLocalRef(clock);
- env->DeleteLocalRef(measurementArray);
+ translateAndSetGnssData(data);
return Void();
}
Return<void> GnssMeasurementCallback::GnssMeasurementCb(
const IGnssMeasurementCallback_V1_0::GnssData& data) {
+ translateAndSetGnssData(data);
+ return Void();
+}
+
+template<class T>
+void GnssMeasurementCallback::translateAndSetGnssData(const T& data) {
JNIEnv* env = getJniEnv();
jobject clock;
jobjectArray measurementArray;
clock = translateGnssClock(env, &data.clock);
- measurementArray = translateGnssMeasurements(
- env, nullptr, data.measurements.data(), data.measurementCount);
+ size_t count = getMeasurementCount(data);
+ measurementArray = translateAllGnssMeasurements(env, data.measurements.data(), count);
setMeasurementData(env, clock, measurementArray);
env->DeleteLocalRef(clock);
env->DeleteLocalRef(measurementArray);
- return Void();
}
-// preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
-void GnssMeasurementCallback::translateGnssMeasurement_V1_0(
+template<>
+size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_0::GnssData>
+ (const IGnssMeasurementCallback_V1_0::GnssData& data) {
+ return data.measurementCount;
+}
+
+template<>
+size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_1::GnssData>
+ (const IGnssMeasurementCallback_V1_1::GnssData& data) {
+ return data.measurements.size();
+}
+
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+ <IGnssMeasurementCallback_V1_0::GnssMeasurement>(
const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
JavaObject& object) {
uint32_t flags = static_cast<uint32_t>(measurement->flags);
@@ -852,6 +863,20 @@
}
}
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+ <IGnssMeasurementCallback_V1_1::GnssMeasurement>(
+ const IGnssMeasurementCallback_V1_1::GnssMeasurement* measurement_V1_1,
+ JavaObject& object) {
+ translateSingleGnssMeasurement(&(measurement_V1_1->v1_0), object);
+
+ // Set the V1_1 flag, and mark that new field has valid information for Java Layer
+ SET(AccumulatedDeltaRangeState,
+ (static_cast<int32_t>(measurement_V1_1->accumulatedDeltaRangeState) |
+ ADR_STATE_HALF_CYCLE_REPORTED));
+}
+
jobject GnssMeasurementCallback::translateGnssClock(
JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock) {
JavaObject object(env, "android/location/GnssClock");
@@ -891,10 +916,10 @@
return object.get();
}
-jobjectArray GnssMeasurementCallback::translateGnssMeasurements(JNIEnv* env,
- const IGnssMeasurementCallback_V1_1::GnssMeasurement* measurements_v1_1,
- const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurements_v1_0,
- size_t count) {
+template<class T>
+jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env,
+ const T* measurements,
+ size_t count) {
if (count == 0) {
return nullptr;
}
@@ -907,17 +932,7 @@
for (uint16_t i = 0; i < count; ++i) {
JavaObject object(env, "android/location/GnssMeasurement");
- if (measurements_v1_1 != nullptr) {
- translateGnssMeasurement_V1_0(&(measurements_v1_1[i].v1_0), object);
-
- // Set the V1_1 flag, and mark that new field has valid information for Java Layer
- SET(AccumulatedDeltaRangeState,
- (static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState) |
- ADR_STATE_HALF_CYCLE_REPORTED));
- } else {
- translateGnssMeasurement_V1_0(&(measurements_v1_0[i]), object);
- }
-
+ translateSingleGnssMeasurement(&(measurements[i]), object);
env->SetObjectArrayElement(gnssMeasurementArray, i, object.get());
}
diff --git a/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp b/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
index bc13fde..9dd6032 100644
--- a/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
+++ b/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
@@ -27,10 +27,6 @@
#include <gatekeeper/password_handle.h>
-extern "C" {
-#include "crypto_scrypt.h"
-}
-
namespace android {
static jlong android_server_SyntheticPasswordManager_nativeSidFromPasswordHandle(JNIEnv* env, jobject, jbyteArray handleArray) {
@@ -48,38 +44,9 @@
}
}
-static jbyteArray android_server_SyntheticPasswordManager_nativeScrypt(JNIEnv* env, jobject, jbyteArray password, jbyteArray salt, jint N, jint r, jint p, jint outLen) {
- if (!password || !salt) {
- return NULL;
- }
-
- int passwordLen = env->GetArrayLength(password);
- int saltLen = env->GetArrayLength(salt);
- jbyteArray ret = env->NewByteArray(outLen);
-
- jbyte* passwordPtr = (jbyte*)env->GetByteArrayElements(password, NULL);
- jbyte* saltPtr = (jbyte*)env->GetByteArrayElements(salt, NULL);
- jbyte* retPtr = (jbyte*)env->GetByteArrayElements(ret, NULL);
-
- int rc = crypto_scrypt((const uint8_t *)passwordPtr, passwordLen,
- (const uint8_t *)saltPtr, saltLen, N, r, p, (uint8_t *)retPtr,
- outLen);
- env->ReleaseByteArrayElements(password, passwordPtr, JNI_ABORT);
- env->ReleaseByteArrayElements(salt, saltPtr, JNI_ABORT);
- env->ReleaseByteArrayElements(ret, retPtr, 0);
-
- if (!rc) {
- return ret;
- } else {
- SLOGE("scrypt failed");
- return NULL;
- }
-}
-
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"nativeSidFromPasswordHandle", "([B)J", (void*)android_server_SyntheticPasswordManager_nativeSidFromPasswordHandle},
- {"nativeScrypt", "([B[BIIII)[B", (void*)android_server_SyntheticPasswordManager_nativeScrypt},
};
int register_android_server_SyntheticPasswordManager(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index b9dabb9..d8225b3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -81,7 +81,8 @@
}
@Override
- public void setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
+ public int setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
+ return DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
}
@Override
@@ -98,6 +99,11 @@
public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
@Override
+ public int getPasswordComplexity() {
+ return DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+ }
+
+ @Override
public void installUpdateFromFile(ComponentName admin,
ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
@@ -120,4 +126,25 @@
int userHandle) {
return false;
}
+
+ @Override
+ public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isManagedKiosk() {
+ return false;
+ }
+
+ @Override
+ public boolean isUnattendedManagedKiosk() {
+ return false;
+ }
+
+ @Override
+ public boolean startViewCalendarEventInManagedProfile(String packageName, long eventId,
+ long start, long end, boolean allDay, int flags) {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6fbb850..7186cdf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import static android.Manifest.permission.BIND_DEVICE_ADMIN;
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
@@ -40,10 +41,13 @@
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_SELECTION;
import static android.app.admin.DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP;
import static android.app.admin.DevicePolicyManager.DELEGATION_INSTALL_EXISTING_PACKAGE;
import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES;
+import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING;
import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_INSTALLATION;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
@@ -53,12 +57,15 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_SUCCESS;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
@@ -109,7 +116,9 @@
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyCache;
+import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.NetworkEvent;
import android.app.admin.PasswordMetrics;
@@ -121,6 +130,7 @@
import android.app.backup.IBackupManager;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -181,6 +191,7 @@
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
+import android.provider.CalendarContract;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
import android.provider.Settings;
@@ -195,6 +206,7 @@
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
@@ -205,7 +217,6 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.StatsLog;
import android.util.Xml;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -358,9 +369,24 @@
DELEGATION_PACKAGE_ACCESS,
DELEGATION_PERMISSION_GRANT,
DELEGATION_INSTALL_EXISTING_PACKAGE,
- DELEGATION_KEEP_UNINSTALLED_PACKAGES
+ DELEGATION_KEEP_UNINSTALLED_PACKAGES,
+ DELEGATION_NETWORK_LOGGING,
+ DELEGATION_CERT_SELECTION,
+ DELEGATION_PACKAGE_INSTALLATION
};
+ // Subset of delegations that can only be delegated by Device Owner.
+ private static final List<String> DEVICE_OWNER_DELEGATIONS = Arrays.asList(new String[] {
+ DELEGATION_NETWORK_LOGGING,
+ DELEGATION_PACKAGE_INSTALLATION
+ });
+
+ // Subset of delegations that only one single package within a given user can hold
+ private static final List<String> EXCLUSIVE_DELEGATIONS = Arrays.asList(new String[] {
+ DELEGATION_NETWORK_LOGGING,
+ DELEGATION_CERT_SELECTION,
+ });
+
/**
* System property whose value is either "true" or "false", indicating whether
* device owner is present.
@@ -445,6 +471,12 @@
private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
/**
+ * The amount of ms that a managed kiosk must go without user interaction to be considered
+ * unattended.
+ */
+ private static final int UNATTENDED_MANAGED_KIOSK_MS = 30000;
+
+ /**
* Strings logged with {@link
* com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}.
*/
@@ -4051,6 +4083,12 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_QUALITY)
+ .setAdmin(who)
+ .setInt(quality)
+ .setBoolean(parent)
+ .write();
}
/**
@@ -4164,6 +4202,11 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LENGTH)
+ .setAdmin(who)
+ .setInt(length)
+ .write();
}
@Override
@@ -4391,6 +4434,11 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_UPPER_CASE)
+ .setAdmin(who)
+ .setInt(length)
+ .write();
}
@Override
@@ -4414,6 +4462,11 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LOWER_CASE)
+ .setAdmin(who)
+ .setInt(length)
+ .write();
}
@Override
@@ -4440,6 +4493,11 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LETTERS)
+ .setAdmin(who)
+ .setInt(length)
+ .write();
}
@Override
@@ -4466,6 +4524,11 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NUMERIC)
+ .setAdmin(who)
+ .setInt(length)
+ .write();
}
@Override
@@ -4492,6 +4555,11 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_SYMBOLS)
+ .setAdmin(who)
+ .setInt(length)
+ .write();
}
@Override
@@ -4518,6 +4586,11 @@
}
maybeLogPasswordComplexitySet(who, userId, parent, metrics);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NON_LETTER)
+ .setAdmin(who)
+ .setInt(length)
+ .write();
}
@Override
@@ -4664,6 +4737,22 @@
}
@Override
+ @PasswordComplexity
+ public int getPasswordComplexity() {
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ enforceUserUnlocked(callingUserId);
+ mContext.enforceCallingOrSelfPermission(
+ GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY,
+ "Must have " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY + " permission.");
+
+ synchronized (getLockObject()) {
+ int targetUserId = getCredentialOwner(callingUserId, /* parent= */ false);
+ PasswordMetrics metrics = getUserPasswordMetricsLocked(targetUserId);
+ return metrics == null ? PASSWORD_COMPLEXITY_NONE : metrics.determineComplexity();
+ }
+ }
+
+ @Override
public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
enforceFullCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
@@ -4890,14 +4979,16 @@
private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
int flags, int callingUid, int userHandle) {
int quality;
+ final int realQuality;
synchronized (getLockObject()) {
quality = getPasswordQuality(null, userHandle, /* parent */ false);
if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
quality = PASSWORD_QUALITY_UNSPECIFIED;
}
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
+ realQuality = metrics.quality;
if (quality != PASSWORD_QUALITY_UNSPECIFIED) {
- final int realQuality = metrics.quality;
+
if (realQuality < quality
&& quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
Slog.w(LOG_TAG, "resetPassword: password quality 0x"
@@ -4984,7 +5075,7 @@
try {
if (token == null) {
if (!TextUtils.isEmpty(password)) {
- mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
+ mLockPatternUtils.saveLockPassword(password, null, realQuality, userHandle);
} else {
mLockPatternUtils.clearLock(null, userHandle);
}
@@ -4993,7 +5084,7 @@
result = mLockPatternUtils.setLockCredentialWithToken(password,
TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE
: LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- quality, tokenHandle, token, userHandle);
+ realQuality, tokenHandle, token, userHandle);
}
boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
if (requireEntry) {
@@ -5094,7 +5185,7 @@
mInjector.binderRestoreCallingIdentity(ident);
}
- mInjector.getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
+ getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
UserHandle.USER_SYSTEM, timeMs);
}
@@ -5113,7 +5204,7 @@
}
policy.mLastMaximumTimeToLock = timeMs;
- mInjector.getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
+ getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
userId, policy.mLastMaximumTimeToLock);
}
@@ -5285,12 +5376,17 @@
mInjector.binderRestoreCallingIdentity(ident);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.LOCK_NOW)
+ .setInt(flags)
+ .write();
}
@Override
public void enforceCanManageCaCerts(ComponentName who, String callerPackage) {
if (who == null) {
- if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
+ if (!isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
+ DELEGATION_CERT_INSTALL)) {
mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
}
} else {
@@ -5363,6 +5459,12 @@
final long id = mInjector.binderClearCallingIdentity();
try {
alias = mCertificateMonitor.installCaCert(userHandle, certBuffer);
+ final boolean isDelegate = (admin == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.INSTALL_CA_CERT)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .write();
if (alias == null) {
Log.w(LOG_TAG, "Problem installing cert");
return false;
@@ -5422,6 +5524,12 @@
keyChain.setGrant(callingUid, alias, true);
}
keyChain.setUserSelectable(alias, isUserSelectable);
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.INSTALL_KEY_PAIR)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .write();
return true;
} catch (RemoteException e) {
Log.e(LOG_TAG, "Installing certificate", e);
@@ -5697,13 +5805,22 @@
}
Intent intent = new Intent(DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS);
- intent.setComponent(aliasChooser);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, uid);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI, uri);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ final ComponentName delegateReceiver;
+ delegateReceiver = resolveDelegateReceiver(DELEGATION_CERT_SELECTION,
+ DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS, caller.getIdentifier());
+
+ if (delegateReceiver != null) {
+ intent.setComponent(delegateReceiver);
+ } else {
+ intent.setComponent(aliasChooser);
+ }
+
final long id = mInjector.binderClearCallingIdentity();
try {
mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() {
@@ -5763,22 +5880,26 @@
*/
@Override
public void setDelegatedScopes(ComponentName who, String delegatePackage,
- List<String> scopes) throws SecurityException {
+ List<String> scopeList) throws SecurityException {
Preconditions.checkNotNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty");
- Preconditions.checkCollectionElementsNotNull(scopes, "Scopes");
+ Preconditions.checkCollectionElementsNotNull(scopeList, "Scopes");
// Remove possible duplicates.
- scopes = new ArrayList(new ArraySet(scopes));
+ final ArrayList<String> scopes = new ArrayList(new ArraySet(scopeList));
// Ensure given scopes are valid.
if (scopes.retainAll(Arrays.asList(DELEGATIONS))) {
throw new IllegalArgumentException("Unexpected delegation scopes");
}
-
+ final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
// Retrieve the user ID of the calling process.
final int userId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
// Ensure calling process is device/profile owner.
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (hasDoDelegation) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ } else {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
// Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
if (shouldCheckIfDelegatePackageIsInstalled(delegatePackage,
getTargetSdk(who.getPackageName(), userId), scopes)) {
@@ -5791,31 +5912,57 @@
// Set the new delegate in user policies.
final DevicePolicyData policy = getUserData(userId);
+ List<String> exclusiveScopes = null;
if (!scopes.isEmpty()) {
policy.mDelegationMap.put(delegatePackage, new ArrayList<>(scopes));
+ exclusiveScopes = new ArrayList<>(scopes);
+ exclusiveScopes.retainAll(EXCLUSIVE_DELEGATIONS);
} else {
// Remove any delegation info if the given scopes list is empty.
policy.mDelegationMap.remove(delegatePackage);
}
+ sendDelegationChangedBroadcast(delegatePackage, scopes, userId);
- // Notify delegate package of updates.
- final Intent intent = new Intent(
- DevicePolicyManager.ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED);
- // Only call receivers registered with Context#registerReceiver (don’t wake delegate).
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- // Limit components this intent resolves to to the delegate package.
- intent.setPackage(delegatePackage);
- // Include the list of delegated scopes as an extra.
- intent.putStringArrayListExtra(DevicePolicyManager.EXTRA_DELEGATION_SCOPES,
- (ArrayList<String>) scopes);
- // Send the broadcast.
- mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ // If set, remove exclusive scopes from all other delegates
+ if (exclusiveScopes != null && !exclusiveScopes.isEmpty()) {
+ for (Map.Entry<String, List<String>> entry : policy.mDelegationMap.entrySet()) {
+ final String currentPackage = entry.getKey();
+ final List<String> currentScopes = entry.getValue();
+ if (!currentPackage.equals(delegatePackage)) {
+ // Iterate through all other delegates
+ if (currentScopes.removeAll(exclusiveScopes)) {
+ // And if this delegate had some exclusive scopes which are now moved
+ // to the new delegate, notify about its delegation changes.
+ if (currentScopes.isEmpty()) {
+ policy.mDelegationMap.remove(currentPackage);
+ }
+ sendDelegationChangedBroadcast(currentPackage,
+ new ArrayList<>(currentScopes), userId);
+ }
+ }
+ }
+ }
// Persist updates.
saveSettingsLocked(userId);
}
}
+ private void sendDelegationChangedBroadcast(String delegatePackage, ArrayList<String> scopes,
+ int userId) {
+ // Notify delegate package of updates.
+ final Intent intent = new Intent(
+ DevicePolicyManager.ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED);
+ // Only call receivers registered with Context#registerReceiver (don’t wake delegate).
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ // Limit components this intent resolves to to the delegate package.
+ intent.setPackage(delegatePackage);
+ // Include the list of delegated scopes as an extra.
+ intent.putStringArrayListExtra(DevicePolicyManager.EXTRA_DELEGATION_SCOPES, scopes);
+ // Send the broadcast.
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ }
+
/**
* Get the delegation scopes given to a delegate package by a device owner or profile owner.
*
@@ -5883,17 +6030,59 @@
synchronized (getLockObject()) {
// Ensure calling process is device/profile owner.
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- final DevicePolicyData policy = getUserData(userId);
+ return getDelegatePackagesInternalLocked(scope, userId);
+ }
+ }
- // Create a list to hold the resulting delegate packages.
- final List<String> delegatePackagesWithScope = new ArrayList<>();
- // Add all delegations containing scope to the result list.
- for (int i = 0; i < policy.mDelegationMap.size(); i++) {
- if (policy.mDelegationMap.valueAt(i).contains(scope)) {
- delegatePackagesWithScope.add(policy.mDelegationMap.keyAt(i));
- }
+ private List<String> getDelegatePackagesInternalLocked(String scope, int userId) {
+ final DevicePolicyData policy = getUserData(userId);
+
+ // Create a list to hold the resulting delegate packages.
+ final List<String> delegatePackagesWithScope = new ArrayList<>();
+ // Add all delegations containing scope to the result list.
+ for (int i = 0; i < policy.mDelegationMap.size(); i++) {
+ if (policy.mDelegationMap.valueAt(i).contains(scope)) {
+ delegatePackagesWithScope.add(policy.mDelegationMap.keyAt(i));
}
- return delegatePackagesWithScope;
+ }
+ return delegatePackagesWithScope;
+ }
+
+ /**
+ * Return the ComponentName of the receiver that handles the given broadcast action, from
+ * the app that holds the given delegation capability. If the app defines multiple receivers
+ * with the same intent action filter, will return any one of them nondeterministically.
+ *
+ * @return ComponentName of the receiver or {@null} if none exists.
+ */
+ private ComponentName resolveDelegateReceiver(String scope, String action, int userId) {
+
+ final List<String> delegates;
+ synchronized (getLockObject()) {
+ delegates = getDelegatePackagesInternalLocked(scope, userId);
+ }
+ if (delegates.size() != 1) {
+ Slog.wtf(LOG_TAG, "More than one delegate holds " + scope);
+ return null;
+ }
+ final String pkg = delegates.get(0);
+ Intent intent = new Intent(action);
+ intent.setPackage(pkg);
+ final List<ResolveInfo> receivers;
+ try {
+ receivers = mIPackageManager.queryIntentReceivers(
+ intent, null, 0, userId).getList();
+ } catch (RemoteException e) {
+ return null;
+ }
+ final int count = receivers.size();
+ if (count >= 1) {
+ if (count > 1) {
+ Slog.w(LOG_TAG, pkg + " defines more than one delegate receiver for " + action);
+ }
+ return receivers.get(0).activityInfo.getComponentName();
+ } else {
+ return null;
}
}
@@ -5910,15 +6099,14 @@
* @param scope the delegation scope to be checked.
* @return {@code true} if the calling process is a delegate of {@code scope}.
*/
- private boolean isCallerDelegate(String callerPackage, String scope) {
+ private boolean isCallerDelegate(String callerPackage, int callerUid, String scope) {
Preconditions.checkNotNull(callerPackage, "callerPackage is null");
if (!Arrays.asList(DELEGATIONS).contains(scope)) {
throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
}
// Retrieve the UID and user ID of the calling process.
- final int callingUid = mInjector.binderGetCallingUid();
- final int userId = UserHandle.getUserId(callingUid);
+ final int userId = UserHandle.getUserId(callerUid);
synchronized (getLockObject()) {
// Retrieve user policy data.
final DevicePolicyData policy = getUserData(userId);
@@ -5931,7 +6119,7 @@
final int uid = mInjector.getPackageManager()
.getPackageUidAsUser(callerPackage, userId);
// Return true if the caller is actually callerPackage.
- return uid == callingUid;
+ return uid == callerUid;
} catch (NameNotFoundException e) {
// Ignore.
}
@@ -5956,15 +6144,34 @@
*/
private void enforceCanManageScope(ComponentName who, String callerPackage, int reqPolicy,
String scope) {
+ enforceCanManageScopeOrCheckPermission(who, callerPackage, reqPolicy, scope, null);
+ }
+
+ /**
+ * Throw a security exception if a ComponentName is given and it is not a device/profile owner
+ * OR if the calling process is not a delegate of the given scope and does not hold the
+ * required permission.
+ */
+ private void enforceCanManageScopeOrCheckPermission(@Nullable ComponentName who,
+ @NonNull String callerPackage, int reqPolicy, @NonNull String scope,
+ @Nullable String permission) {
// If a ComponentName is given ensure it is a device or profile owner according to policy.
if (who != null) {
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(who, reqPolicy);
}
- // If no ComponentName is given ensure calling process has scope delegation.
- } else if (!isCallerDelegate(callerPackage, scope)) {
- throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid()
- + " is not a delegate of scope " + scope + ".");
+ } else {
+ // If no ComponentName is given ensure calling process has scope delegation or required
+ // permission
+ if (isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(), scope)) {
+ return;
+ }
+ if (permission == null) {
+ throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid()
+ + " is not a delegate of scope " + scope + ".");
+ } else {
+ mContext.enforceCallingOrSelfPermission(permission, null);
+ }
}
}
@@ -6116,6 +6323,11 @@
synchronized (getLockObject()) {
admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON)
+ .setAdmin(admin.info.getComponent())
+ .setInt(flags)
+ .write();
String internalReason = "DevicePolicyManager.wipeDataWithReason() from "
+ admin.info.getComponent().flattenToShortString();
wipeDataNoLock(
@@ -6898,9 +7110,16 @@
}
}
- private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who) throws SecurityException {
+ private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who)
+ throws SecurityException {
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+ ensureAllUsersAffiliated();
+ }
+
+ private void ensureAllUsersAffiliated() throws SecurityException {
+ synchronized (getLockObject()) {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
throw new SecurityException("Not all users are affiliated.");
}
@@ -6959,14 +7178,22 @@
}
void sendDeviceOwnerCommand(String action, Bundle extras) {
- int deviceOwnerUserId;
- ComponentName deviceOwnerComponent;
+ final int deviceOwnerUserId;
synchronized (getLockObject()) {
deviceOwnerUserId = mOwners.getDeviceOwnerUserId();
- deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
}
- sendActiveAdminCommand(action, extras, deviceOwnerUserId,
- deviceOwnerComponent);
+
+ ComponentName receiverComponent = null;
+ if (action.equals(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE)) {
+ receiverComponent = resolveDelegateReceiver(DELEGATION_NETWORK_LOGGING, action,
+ deviceOwnerUserId);
+ }
+ if (receiverComponent == null) {
+ synchronized (getLockObject()) {
+ receiverComponent = mOwners.getDeviceOwnerComponent();
+ }
+ }
+ sendActiveAdminCommand(action, extras, deviceOwnerUserId, receiverComponent);
}
private void sendProfileOwnerCommand(String action, Bundle extras, int userHandle) {
@@ -7220,6 +7447,12 @@
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET,
who.getPackageName(), userHandle, affectedUserId, which);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_KEYGUARD_DISABLED_FEATURES)
+ .setAdmin(who)
+ .setInt(which)
+ .setBoolean(parent)
+ .write();
}
/**
@@ -8417,7 +8650,8 @@
@Override
public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) {
- return isCallerDelegate(callerPackage, DELEGATION_APP_RESTRICTIONS);
+ return isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
+ DELEGATION_APP_RESTRICTIONS);
}
@Override
@@ -9466,7 +9700,14 @@
}
saveUserRestrictionsLocked(userHandle);
}
- StatsLog.write(StatsLog.USER_RESTRICTION_CHANGED, key, enabledFromThisOwner);
+ final int eventId = enabledFromThisOwner
+ ? DevicePolicyEnums.ADD_USER_RESTRICTION
+ : DevicePolicyEnums.REMOVE_USER_RESTRICTION;
+ DevicePolicyEventLogger
+ .createEvent(eventId)
+ .setAdmin(who)
+ .setStrings(key)
+ .write();
if (SecurityLog.isLoggingEnabled()) {
final int eventTag = enabledFromThisOwner
? SecurityLog.TAG_USER_RESTRICTION_ADDED
@@ -10238,6 +10479,11 @@
try {
setUserRestriction(who, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
(Integer.parseInt(value) == 0) ? true : false);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
+ .setAdmin(who)
+ .setStrings(setting, value)
+ .write();
} catch (NumberFormatException exc) {
Slog.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
}
@@ -10265,6 +10511,11 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
+ .setAdmin(who)
+ .setStrings(setting, value)
+ .write();
}
@Override
@@ -10619,10 +10870,32 @@
}
@Override
+ public boolean canSilentlyInstallPackage(String callerPackage, int callerUid) {
+ if (callerPackage == null) {
+ return false;
+ }
+ if (isUserAffiliatedWithDevice(UserHandle.getUserId(callerUid))
+ && isActiveAdminWithPolicy(callerUid,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) {
+ // device owner or a profile owner affiliated with the device owner
+ return true;
+ }
+ if (DevicePolicyManagerService.this.isCallerDelegate(callerPackage, callerUid,
+ DELEGATION_PACKAGE_INSTALLATION)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public void reportSeparateProfileChallengeChanged(@UserIdInt int userId) {
synchronized (getLockObject()) {
updateMaximumTimeToLockLocked(userId);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SEPARATE_PROFILE_CHALLENGE_CHANGED)
+ .setBoolean(isSeparateProfileChallengeEnabled(userId))
+ .write();
}
@Override
@@ -10963,6 +11236,13 @@
saveSettingsLocked(userId);
}
}
+ final boolean isDelegate = (admin == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PERMISSION_POLICY)
+ .setAdmin(callerPackage)
+ .setInt(policy)
+ .setBoolean(isDelegate)
+ .write();
}
@Override
@@ -11014,7 +11294,6 @@
PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0, user);
} break;
}
- return true;
} catch (SecurityException se) {
return false;
} catch (NameNotFoundException e) {
@@ -11023,6 +11302,15 @@
mInjector.binderRestoreCallingIdentity(ident);
}
}
+ final boolean isDelegate = (admin == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PERMISSION_GRANT_STATE)
+ .setAdmin(callerPackage)
+ .setStrings(permission)
+ .setInt(grantState)
+ .setBoolean(isDelegate)
+ .write();
+ return true;
}
@Override
@@ -11848,6 +12136,11 @@
mSecurityLogMonitor.stop();
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_SECURITY_LOGGING_ENABLED)
+ .setAdmin(admin)
+ .setBoolean(enabled)
+ .write();
}
@Override
@@ -11885,13 +12178,17 @@
Preconditions.checkNotNull(admin);
ensureDeviceOwnerAndAllUsersAffiliated(admin);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RETRIEVE_PRE_REBOOT_SECURITY_LOGS)
+ .setAdmin(admin)
+ .write();
+
if (!mContext.getResources().getBoolean(R.bool.config_supportPreRebootSecurityLogs)
|| !mInjector.securityLogGetLoggingEnabledProperty()) {
return null;
}
recordSecurityLogRetrievalTime();
-
ArrayList<SecurityEvent> output = new ArrayList<SecurityEvent>();
try {
SecurityLog.readPreviousEvents(output);
@@ -11918,6 +12215,10 @@
recordSecurityLogRetrievalTime();
List<SecurityEvent> logs = mSecurityLogMonitor.retrieveLogs();
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RETRIEVE_SECURITY_LOGS)
+ .setAdmin(admin)
+ .write();
return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
}
@@ -12379,13 +12680,14 @@
}
@Override
- public void setNetworkLoggingEnabled(ComponentName admin, boolean enabled) {
+ public void setNetworkLoggingEnabled(@Nullable ComponentName admin,
+ @NonNull String packageName, boolean enabled) {
if (!mHasFeature) {
return;
}
synchronized (getLockObject()) {
- Preconditions.checkNotNull(admin);
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ enforceCanManageScope(admin, packageName, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ DELEGATION_NETWORK_LOGGING);
if (enabled == isNetworkLoggingEnabledInternalLocked()) {
// already in the requested state
@@ -12486,12 +12788,15 @@
}
@Override
- public boolean isNetworkLoggingEnabled(ComponentName admin) {
+ public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin,
+ @NonNull String packageName) {
if (!mHasFeature) {
return false;
}
synchronized (getLockObject()) {
- enforceDeviceOwnerOrManageUsers();
+ enforceCanManageScopeOrCheckPermission(admin, packageName,
+ DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, DELEGATION_NETWORK_LOGGING,
+ android.Manifest.permission.MANAGE_USERS);
return isNetworkLoggingEnabledInternalLocked();
}
}
@@ -12509,12 +12814,14 @@
* @see NetworkLoggingHandler#MAX_EVENTS_PER_BATCH
*/
@Override
- public List<NetworkEvent> retrieveNetworkLogs(ComponentName admin, long batchToken) {
+ public List<NetworkEvent> retrieveNetworkLogs(@Nullable ComponentName admin,
+ @NonNull String packageName, long batchToken) {
if (!mHasFeature) {
return null;
}
- Preconditions.checkNotNull(admin);
- ensureDeviceOwnerAndAllUsersAffiliated(admin);
+ enforceCanManageScope(admin, packageName, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ DELEGATION_NETWORK_LOGGING);
+ ensureAllUsersAffiliated();
synchronized (getLockObject()) {
if (mNetworkLogger == null
@@ -13278,32 +13585,40 @@
}
@Override
- public void setGlobalPrivateDns(@NonNull ComponentName who, int mode, String privateDnsHost) {
+ public int setGlobalPrivateDns(@NonNull ComponentName who, int mode, String privateDnsHost) {
if (!mHasFeature) {
- return;
+ return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
}
Preconditions.checkNotNull(who, "ComponentName is null");
enforceDeviceOwner(who);
+ final int returnCode;
+
switch (mode) {
case PRIVATE_DNS_MODE_OPPORTUNISTIC:
if (!TextUtils.isEmpty(privateDnsHost)) {
- throw new IllegalArgumentException("A DNS host should not be provided when " +
- "setting opportunistic mode.");
+ throw new IllegalArgumentException(
+ "Host provided for opportunistic mode, but is not needed.");
}
putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC, null);
- break;
+ return PRIVATE_DNS_SET_SUCCESS;
case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
- if (!NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+ if (TextUtils.isEmpty(privateDnsHost)
+ || !NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
throw new IllegalArgumentException(
- String.format("Provided hostname is not valid: %s", privateDnsHost));
+ String.format("Provided hostname %s is not valid", privateDnsHost));
}
- putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+
+ // Connectivity check will have been performed in the DevicePolicyManager before
+ // the call here.
+ putPrivateDnsSettings(
+ ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
privateDnsHost);
- break;
+ return PRIVATE_DNS_SET_SUCCESS;
default:
- throw new IllegalArgumentException(String.format("Unsupported mode: %d", mode));
+ throw new IllegalArgumentException(
+ String.format("Provided mode, %d, is not a valid mode.", mode));
}
}
@@ -13426,10 +13741,155 @@
enforceCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
- if (admin != null && admin.mCrossProfileCalendarPackages != null) {
+ if (admin != null) {
return admin.mCrossProfileCalendarPackages.contains(packageName);
}
}
return false;
}
+
+ @Override
+ public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
+ if (!mHasFeature) {
+ return Collections.emptyList();
+ }
+ enforceCrossUsersPermission(userHandle);
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+ if (admin != null) {
+ return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isManagedKiosk() {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceManageUsers();
+ long id = mInjector.binderClearCallingIdentity();
+ try {
+ return isManagedKioskInternal();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ @Override
+ public boolean isUnattendedManagedKiosk() {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceManageUsers();
+ long id = mInjector.binderClearCallingIdentity();
+ try {
+ return isManagedKioskInternal()
+ && getPowerManagerInternal().wasDeviceIdleFor(UNATTENDED_MANAGED_KIOSK_MS);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ /**
+ * Returns whether the device is currently being used as a publicly-accessible dedicated device.
+ * Assumes that feature checks and permission checks have already been performed, and that the
+ * calling identity has been cleared.
+ */
+ private boolean isManagedKioskInternal() throws RemoteException {
+ return mOwners.hasDeviceOwner()
+ && mInjector.getIActivityManager().getLockTaskModeState()
+ == ActivityManager.LOCK_TASK_MODE_LOCKED
+ && !isLockTaskFeatureEnabled(DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO)
+ && !deviceHasKeyguard()
+ && !inEphemeralUserSession();
+ }
+
+ private boolean isLockTaskFeatureEnabled(int lockTaskFeature) throws RemoteException {
+ int lockTaskFeatures =
+ getUserData(mInjector.getIActivityManager().getCurrentUser().id).mLockTaskFeatures;
+ return (lockTaskFeatures & lockTaskFeature) == lockTaskFeature;
+ }
+
+ private boolean deviceHasKeyguard() {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ if (mLockPatternUtils.isSecure(userInfo.id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean inEphemeralUserSession() {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ if (mInjector.getUserManager().isUserEphemeral(userInfo.id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private PowerManagerInternal getPowerManagerInternal() {
+ return mInjector.getPowerManagerInternal();
+ }
+
+ @Override
+ public boolean startViewCalendarEventInManagedProfile(String packageName, long eventId,
+ long start, long end, boolean allDay, int flags) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkStringNotEmpty(packageName, "Package name is empty");
+
+ final int callingUid = mInjector.binderGetCallingUid();
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ if (!isCallingFromPackage(packageName, callingUid)) {
+ throw new SecurityException("Input package name doesn't align with actual "
+ + "calling package.");
+ }
+ final long identity = mInjector.binderClearCallingIdentity();
+ try {
+ final int workProfileUserId = getManagedUserId(callingUserId);
+ if (workProfileUserId < 0) {
+ return false;
+ }
+ if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) {
+ Log.d(LOG_TAG, String.format("Package %s is not allowed to access cross-profile"
+ + "calendar APIs", packageName));
+ return false;
+ }
+ final Intent intent = new Intent(CalendarContract.ACTION_VIEW_WORK_CALENDAR_EVENT);
+ intent.setPackage(packageName);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_ID, eventId);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, allDay);
+ intent.setFlags(flags);
+ try {
+ mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId));
+ } catch (ActivityNotFoundException e) {
+ Log.e(LOG_TAG, "View event activity not found", e);
+ return false;
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(identity);
+ }
+ return true;
+ }
+
+ private boolean isCallingFromPackage(String packageName, int callingUid) {
+ try {
+ final int packageUid = mInjector.getPackageManager().getPackageUidAsUser(
+ packageName, UserHandle.getUserId(callingUid));
+ return packageUid == callingUid;
+ } catch (NameNotFoundException e) {
+ Log.d(LOG_TAG, "Calling package not found", e);
+ return false;
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
index 7910598..d8a875d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
+import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.StartInstallingUpdateCallback;
import android.content.Context;
@@ -26,6 +27,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.util.Log;
import java.io.File;
@@ -132,6 +134,10 @@
protected void notifyCallbackOnError(int errorCode, String errorMessage) {
cleanupUpdateFile();
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE_ERROR)
+ .setInt(errorCode)
+ .write();
try {
mCallback.onStartInstallingUpdateError(errorCode, errorMessage);
} catch (RemoteException e) {
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
deleted file mode 100644
index 108f91c..0000000
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.intelligence;
-
-import android.annotation.NonNull;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.IBinder;
-import android.service.intelligence.InteractionContext;
-import android.service.intelligence.InteractionSessionId;
-import android.service.intelligence.SmartSuggestionsService;
-import android.service.intelligence.SnapshotData;
-import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
-import android.view.intelligence.ContentCaptureEvent;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-import com.android.server.AbstractRemoteService;
-import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
-import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
-
-import java.io.PrintWriter;
-import java.util.List;
-
-final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks {
-
- private static final String TAG = "ContentCaptureSession";
-
- private final Object mLock;
- final IBinder mActivityToken;
- private final IntelligencePerUserService mService;
- private final RemoteIntelligenceService mRemoteService;
- private final InteractionContext mInterationContext;
- private final InteractionSessionId mId;
- private AugmentedAutofillCallback mAutofillCallback;
-
- ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
- @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
- @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
- int taskId, int displayId, @NonNull InteractionSessionId sessionId, int flags,
- boolean bindInstantServiceAllowed, boolean verbose) {
- mLock = lock;
- mActivityToken = activityToken;
- mService = service;
- mId = Preconditions.checkNotNull(sessionId);
- mRemoteService = new RemoteIntelligenceService(context,
- SmartSuggestionsService.SERVICE_INTERFACE, serviceComponentName, userId, this,
- bindInstantServiceAllowed, verbose);
- mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
- }
-
- /**
- * Returns whether this session is for the given activity.
- */
- boolean isActivitySession(@NonNull IBinder activityToken) {
- return mActivityToken.equals(activityToken);
- }
-
- /**
- * Notifies the {@link SmartSuggestionsService} that the service started.
- */
- @GuardedBy("mLock")
- public void notifySessionStartedLocked() {
- mRemoteService.onSessionLifecycleRequest(mInterationContext, mId);
- }
-
- /**
- * Notifies the {@link SmartSuggestionsService} of a batch of events.
- */
- public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
- mRemoteService.onContentCaptureEventsRequest(mId, events);
- }
-
- /**
- * Notifies the {@link SmartSuggestionsService} of a snapshot of an activity.
- */
- @GuardedBy("mLock")
- public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
- mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
- }
-
- /**
- * Requests the service to autofill the given field.
- */
- public AugmentedAutofillCallback requestAutofillLocked(@NonNull IAutoFillManagerClient client,
- int autofillSessionId, @NonNull AutofillId focusedId) {
- mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId);
- if (mAutofillCallback == null) {
- mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId);
- }
- return mAutofillCallback;
- }
-
- /**
- * Cleans up the session and removes it from the service.
- *
- * @param notifyRemoteService whether it should trigger a {@link
- * SmartSuggestionsService#onDestroyInteractionSession(InteractionSessionId)}
- * request.
- */
- @GuardedBy("mLock")
- public void removeSelfLocked(boolean notifyRemoteService) {
- try {
- destroyLocked(notifyRemoteService);
- } finally {
- mService.removeSessionLocked(mId);
- }
- }
-
- /**
- * Cleans up the session, but not removes it from the service.
- *
- * @param notifyRemoteService whether it should trigger a {@link
- * SmartSuggestionsService#onDestroyInteractionSession(InteractionSessionId)}
- * request.
- */
- @GuardedBy("mLock")
- public void destroyLocked(boolean notifyRemoteService) {
- if (mService.isVerbose()) {
- Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
- }
- if (mAutofillCallback != null) {
- mAutofillCallback.destroy();
- mAutofillCallback = null;
- }
-
- // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
- if (notifyRemoteService) {
- mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
- }
- }
-
- @Override // from RemoteScreenObservationServiceCallbacks
- public void onServiceDied(AbstractRemoteService service) {
- // TODO(b/111276913): implement (remove session from PerUserSession?)
- if (mService.isDebug()) {
- Slog.d(TAG, "onServiceDied() for " + mId);
- }
- synchronized (mLock) {
- removeSelfLocked(/* notifyRemoteService= */ false);
- }
- }
-
- @Override // from RemoteScreenObservationServiceCallbacks
- public void onFailureOrTimeout(boolean timedOut) {
- // TODO(b/111276913): log metrics on whether timed out or not
- if (mService.isDebug()) {
- Slog.d(TAG, "onFailureOrTimeout(" + mId + "): timed out=" + timedOut);
- }
- synchronized (mLock) {
- removeSelfLocked(/* notifyRemoteService= */ false);
- }
- }
-
- @GuardedBy("mLock")
- public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("id: "); mId.dump(pw); pw.println();
- pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println();
- pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
- pw.print(prefix); pw.print("has autofill callback: ");
- pw.println(mAutofillCallback != null);
- }
-
- @Override
- public String toString() {
- return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
- }
-}
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
deleted file mode 100644
index e0d47d2..0000000
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.intelligence;
-
-import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.ActivityManagerInternal;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.UserManager;
-import android.service.intelligence.InteractionSessionId;
-import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
-import android.view.intelligence.ContentCaptureEvent;
-import android.view.intelligence.IIntelligenceManager;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.server.AbstractMasterSystemService;
-import com.android.server.LocalServices;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.List;
-
-/**
- * A service used to observe the contents of the screen.
- *
- * <p>The data collected by this service can be analyzed and combined with other sources to provide
- * contextual data in other areas of the system such as Autofill.
- */
-//TODO(b/111276913): rename once the final name is defined
-public final class IntelligenceManagerService extends
- AbstractMasterSystemService<IntelligenceManagerService, IntelligencePerUserService> {
-
- private static final String TAG = "IntelligenceManagerService";
-
- @GuardedBy("mLock")
- private ActivityManagerInternal mAm;
-
- private final LocalService mLocalService = new LocalService();
-
- public IntelligenceManagerService(Context context) {
- super(context, UserManager.DISALLOW_INTELLIGENCE_CAPTURE);
- }
-
- @Override // from AbstractMasterSystemService
- protected String getServiceSettingsProperty() {
- // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
- return "smart_suggestions_service";
- }
-
- @Override // from AbstractMasterSystemService
- protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
- boolean disabled) {
- return new IntelligencePerUserService(this, mLock, resolvedUserId);
- }
-
- @Override // from SystemService
- public void onStart() {
- publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE,
- new IntelligenceManagerServiceStub());
- publishLocalService(IntelligenceManagerInternal.class, mLocalService);
- }
-
- @Override // from AbstractMasterSystemService
- protected void onServiceRemoved(@NonNull IntelligencePerUserService service,
- @UserIdInt int userId) {
- service.destroyLocked();
- }
-
- private ActivityManagerInternal getAmInternal() {
- synchronized (mLock) {
- if (mAm == null) {
- mAm = LocalServices.getService(ActivityManagerInternal.class);
- }
- }
- return mAm;
- }
-
- final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub {
-
- @Override
- public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
- @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
- int flags, @NonNull IResultReceiver result) {
- Preconditions.checkNotNull(activityToken);
- Preconditions.checkNotNull(componentName);
- Preconditions.checkNotNull(sessionId);
-
- // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
- // so we don't pass it on startSession (same for Autofill)
- final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false);
-
- // TODO(b/111276913): get from AM as well
- final int displayId = 0;
-
- synchronized (mLock) {
- final IntelligencePerUserService service = getServiceForUserLocked(userId);
- service.startSessionLocked(activityToken, componentName, taskId, displayId,
- sessionId, flags, result);
- }
- }
-
- @Override
- public void sendEvents(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- Preconditions.checkNotNull(sessionId);
- Preconditions.checkNotNull(events);
-
- synchronized (mLock) {
- final IntelligencePerUserService service = getServiceForUserLocked(userId);
- service.sendEventsLocked(sessionId, events);
- }
- }
-
- @Override
- public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
- @Nullable List<ContentCaptureEvent> events) {
- Preconditions.checkNotNull(sessionId);
-
- synchronized (mLock) {
- final IntelligencePerUserService service = getServiceForUserLocked(userId);
- service.finishSessionLocked(sessionId, events);
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
-
- synchronized (mLock) {
- dumpLocked("", pw);
- }
- }
- }
-
- private final class LocalService extends IntelligenceManagerInternal {
-
- @Override
- public boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId) {
- synchronized (mLock) {
- final IntelligencePerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- return service.isIntelligenceServiceForUserLocked(uid);
- }
- }
- return false;
- }
-
- @Override
- public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
- @NonNull Bundle data) {
- synchronized (mLock) {
- final IntelligencePerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- return service.sendActivityAssistDataLocked(activityToken, data);
- }
- }
- return false;
- }
-
- @Override
- public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
- @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
- int autofillSessionId, @NonNull AutofillId focusedId) {
- synchronized (mLock) {
- final IntelligencePerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- return service.requestAutofill(client, activityToken, autofillSessionId,
- focusedId);
- }
- }
- return null;
- }
- }
-}
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
deleted file mode 100644
index d9f4f20..0000000
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.intelligence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.RemoteException;
-import android.service.intelligence.ContentCaptureEventsRequest;
-import android.service.intelligence.IIntelligenceService;
-import android.service.intelligence.InteractionContext;
-import android.service.intelligence.InteractionSessionId;
-import android.service.intelligence.SnapshotData;
-import android.text.format.DateUtils;
-import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillManager;
-import android.view.autofill.IAutoFillManagerClient;
-import android.view.intelligence.ContentCaptureEvent;
-
-import com.android.internal.os.IResultReceiver;
-import com.android.server.AbstractMultiplePendingRequestsRemoteService;
-
-import java.util.List;
-
-//TODO(b/111276913): rename once the final name is defined
-final class RemoteIntelligenceService
- extends AbstractMultiplePendingRequestsRemoteService<RemoteIntelligenceService> {
-
- private static final String TAG = "RemoteIntelligenceService";
-
- private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
-
- private final RemoteIntelligenceServiceCallbacks mCallbacks;
- private IIntelligenceService mService;
-
- RemoteIntelligenceService(Context context, String serviceInterface,
- ComponentName componentName, int userId,
- RemoteIntelligenceServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
- boolean verbose) {
- super(context, serviceInterface, componentName, userId, callbacks,
- bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
- mCallbacks = callbacks;
- }
-
- @Override // from RemoteService
- protected IInterface getServiceInterface(@NonNull IBinder service) {
- mService = IIntelligenceService.Stub.asInterface(service);
- return mService;
- }
-
- // TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
- @Override // from RemoteService
- protected long getTimeoutIdleBindMillis() {
- // TODO(b/111276913): read from Settings so it can be changed in the field
- return TIMEOUT_IDLE_BIND_MILLIS;
- }
-
- @Override // from RemoteService
- protected long getRemoteRequestMillis() {
- // TODO(b/111276913): read from Settings so it can be changed in the field
- return TIMEOUT_REMOTE_REQUEST_MILLIS;
- }
-
- /**
- * Called by {@link ContentCaptureSession} to generate a call to the
- * {@link RemoteIntelligenceService} to indicate the session was created (when {@code context}
- * is not {@code null} or destroyed (when {@code context} is {@code null}).
- */
- public void onSessionLifecycleRequest(@Nullable InteractionContext context,
- @NonNull InteractionSessionId sessionId) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId));
- }
-
- /**
- * Called by {@link ContentCaptureSession} to send a batch of events to the service.
- */
- public void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingOnContentCaptureEventsRequest(this, sessionId, events));
- }
-
- /**
- * Called by {@link ContentCaptureSession} to send snapshot data to the service.
- */
- public void onActivitySnapshotRequest(@NonNull InteractionSessionId sessionId,
- @NonNull SnapshotData snapshotData) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
- }
-
- /**
- * Called by {@link ContentCaptureSession} to request augmented autofill.
- */
- public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId,
- @NonNull IAutoFillManagerClient client, int autofillSessionId,
- @NonNull AutofillId focusedId) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId,
- focusedId));
- }
-
- /**
- * Called by {@link ContentCaptureSession} when it's time to destroy all augmented autofill
- * requests.
- */
- public void onDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
- }
-
- private abstract static class MyPendingRequest
- extends PendingRequest<RemoteIntelligenceService> {
- protected final InteractionSessionId mSessionId;
-
- private MyPendingRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId) {
- super(service);
- mSessionId = sessionId;
- }
-
- @Override // from PendingRequest
- protected final void onTimeout(RemoteIntelligenceService remoteService) {
- Slog.w(TAG, "timed out handling " + getClass().getSimpleName() + " for "
- + mSessionId);
- remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ true);
- }
-
- @Override // from PendingRequest
- public final void run() {
- final RemoteIntelligenceService remoteService = getService();
- if (remoteService != null) {
- try {
- // We don't expect the service to call us back, so we finish right away.
- myRun(remoteService);
- // TODO(b/111330312): not true anymore!!
- finish();
- } catch (RemoteException e) {
- Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
- + mSessionId + ": " + e);
- remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ false);
- }
- }
- }
-
- protected abstract void myRun(@NonNull RemoteIntelligenceService service)
- throws RemoteException;
-
- }
-
- private static final class PendingSessionLifecycleRequest extends MyPendingRequest {
-
- private final InteractionContext mContext;
-
- protected PendingSessionLifecycleRequest(@NonNull RemoteIntelligenceService service,
- @Nullable InteractionContext context, @NonNull InteractionSessionId sessionId) {
- super(service, sessionId);
- mContext = context;
- }
-
- @Override // from MyPendingRequest
- public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
- remoteService.mService.onSessionLifecycle(mContext, mSessionId);
- }
- }
-
- private static final class PendingOnContentCaptureEventsRequest extends MyPendingRequest {
-
- private final List<ContentCaptureEvent> mEvents;
-
- protected PendingOnContentCaptureEventsRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- super(service, sessionId);
- mEvents = events;
- }
-
- @Override // from MyPendingRequest
- public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
- remoteService.mService.onContentCaptureEventsRequest(mSessionId,
- new ContentCaptureEventsRequest(mEvents));
- }
- }
-
- private static final class PendingOnActivitySnapshotRequest extends MyPendingRequest {
-
- private final SnapshotData mSnapshotData;
-
- protected PendingOnActivitySnapshotRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId,
- @NonNull SnapshotData snapshotData) {
- super(service, sessionId);
- mSnapshotData = snapshotData;
- }
-
- @Override // from MyPendingRequest
- protected void myRun(@NonNull RemoteIntelligenceService remoteService)
- throws RemoteException {
- remoteService.mService.onActivitySnapshot(mSessionId, mSnapshotData);
- }
- }
-
- private static final class PendingAutofillRequest extends MyPendingRequest {
- private final @NonNull AutofillId mFocusedId;
- private final @NonNull IAutoFillManagerClient mClient;
- private final int mAutofillSessionId;
-
- protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client,
- int autofillSessionId, @NonNull AutofillId focusedId) {
- super(service, sessionId);
- mClient = client;
- mAutofillSessionId = autofillSessionId;
- mFocusedId = focusedId;
- }
-
- @Override // from MyPendingRequest
- public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
- final IResultReceiver receiver = new IResultReceiver.Stub() {
-
- @Override
- public void send(int resultCode, Bundle resultData) throws RemoteException {
- final IBinder realClient = resultData
- .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
- remoteService.mService.onAutofillRequest(mSessionId, realClient,
- mAutofillSessionId, mFocusedId);
- }
- };
-
- // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
- // cache IAugmentedAutofillManagerClient reference, etc...
- mClient.getAugmentedAutofillClient(receiver);
- }
- }
-
- private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
-
- protected PendingDestroyAutofillWindowsRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId) {
- super(service, sessionId);
- }
-
- @Override
- protected void myRun(@NonNull RemoteIntelligenceService service) throws RemoteException {
- service.mService.onDestroyAutofillWindowsRequest(mSessionId);
- // TODO(b/111330312): implement timeout
- }
- }
-
- public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
- // To keep it simple, we use the same callback for all failures / timeouts.
- void onFailureOrTimeout(boolean timedOut);
- }
-}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f3704d8..88f645d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -22,6 +22,7 @@
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.NonNull;
import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -35,7 +36,6 @@
import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
-import android.hardware.display.ColorDisplayManager;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -55,6 +55,9 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
+import android.provider.Settings;
+import android.sysprop.VoldProperties;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
@@ -120,6 +123,7 @@
import com.android.server.role.RoleManagerService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
+import com.android.server.signedconfig.SignedConfigService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.stats.StatsCompanionService;
import com.android.server.statusbar.StatusBarManagerService;
@@ -234,8 +238,8 @@
"com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
private static final String AUTO_FILL_MANAGER_SERVICE_CLASS =
"com.android.server.autofill.AutofillManagerService";
- private static final String INTELLIGENCE_MANAGER_SERVICE_CLASS =
- "com.android.server.intelligence.IntelligenceManagerService";
+ private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS =
+ "com.android.server.contentcapture.ContentCaptureManagerService";
private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
"com.android.server.timezone.RulesManagerService$Lifecycle";
private static final String IOT_SERVICE_CLASS =
@@ -654,7 +658,7 @@
traceEnd();
// Only run "core" apps if we're encrypting the device.
- String cryptState = SystemProperties.get("vold.decrypt");
+ String cryptState = VoldProperties.decrypt().orElse("");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
@@ -796,10 +800,6 @@
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
- //TODO(b/111276913): temporarily disabled until the manager is properly implemented to
- // ignore events when disabled and buffer when enabled
- boolean disableIntelligence = SystemProperties.getBoolean(
- "config.disable_intelligence", true);
boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime",
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -925,7 +925,7 @@
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
- new PhoneWindowManager(), mWindowManagerGlobalLock);
+ new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
@@ -994,6 +994,11 @@
traceBeginAndSlog("PinnerService");
mSystemServiceManager.startService(PinnerService.class);
traceEnd();
+
+ traceBeginAndSlog("SignedConfigService");
+ SignedConfigService.registerUpdateReceiver(mSystemContext);
+ traceEnd();
+
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
@@ -1130,13 +1135,7 @@
traceEnd();
}
- if (!disableIntelligence) {
- traceBeginAndSlog("StartIntelligenceService");
- mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
- traceEnd();
- } else {
- Slog.d(TAG, "IntelligenceService disabled");
- }
+ startContentCaptureService(context);
// NOTE: ClipboardService indirectly depends on IntelligenceService
traceBeginAndSlog("StartClipboardService");
@@ -1398,6 +1397,15 @@
traceEnd();
}
+ // Start ADB Debugging Service
+ traceBeginAndSlog("StartAdbService");
+ try {
+ mSystemServiceManager.startService(ADB_SERVICE_CLASS);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting AdbService");
+ }
+ traceEnd();
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
|| mPackageManager.hasSystemFeature(
PackageManager.FEATURE_USB_ACCESSORY)
@@ -1408,15 +1416,6 @@
traceEnd();
}
- // Start ADB Debugging Service
- traceBeginAndSlog("StartAdbService");
- try {
- mSystemServiceManager.startService(ADB_SERVICE_CLASS);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting AdbService");
- }
- traceEnd();
-
if (!isWatch) {
traceBeginAndSlog("StartSerialService");
try {
@@ -1443,11 +1442,9 @@
mSystemServiceManager.startService(TwilightService.class);
traceEnd();
- if (ColorDisplayManager.isNightDisplayAvailable(context)) {
- traceBeginAndSlog("StartColorDisplay");
- mSystemServiceManager.startService(ColorDisplayService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartColorDisplay");
+ mSystemServiceManager.startService(ColorDisplayService.class);
+ traceEnd();
traceBeginAndSlog("StartJobScheduler");
mSystemServiceManager.startService(JobSchedulerService.class);
@@ -2100,6 +2097,37 @@
}, BOOT_TIMINGS_TRACE_LOG);
}
+ private void startContentCaptureService(@NonNull Context context) {
+
+ // First check if it was explicitly enabled by Settings
+ boolean explicitlySupported = false;
+ final String settings = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED);
+ if (settings != null) {
+ explicitlySupported = Boolean.parseBoolean(settings);
+ if (explicitlySupported) {
+ Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings");
+ } else {
+ Slog.d(TAG, "ContentCaptureService explicitly disabled by Settings");
+ return;
+ }
+ }
+
+ // Then check if OEM overlaid the resource that defines the service.
+ if (!explicitlySupported) {
+ final String serviceName = context
+ .getString(com.android.internal.R.string.config_defaultContentCaptureService);
+ if (TextUtils.isEmpty(serviceName)) {
+ Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
+ return;
+ }
+ }
+
+ traceBeginAndSlog("StartContentCaptureService");
+ mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index ba4caf44..96ef0ce 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -22,7 +22,10 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+import android.annotation.UserIdInt;
import android.app.Application;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -32,11 +35,14 @@
import android.content.Intent;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowBinder;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,6 +50,8 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowContextWrapper;
import java.io.File;
import java.io.FileDescriptor;
@@ -51,15 +59,19 @@
/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBinder.class})
@Presubmit
public class BackupManagerServiceTest {
private static final String TEST_PACKAGE = "package";
private static final String TEST_TRANSPORT = "transport";
+ private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+
+ private ShadowContextWrapper mShadowContext;
@Mock private UserBackupManagerService mUserBackupManagerService;
- @Mock private TransportManager mTransportManager;
private BackupManagerService mBackupManagerService;
private Context mContext;
+ @UserIdInt private int mUserId;
/** Initialize {@link BackupManagerService}. */
@Before
@@ -68,18 +80,26 @@
Application application = RuntimeEnvironment.application;
mContext = application;
+ mShadowContext = shadowOf(application);
+ mUserId = NON_USER_SYSTEM;
mBackupManagerService =
new BackupManagerService(
application,
new Trampoline(application),
- BackupManagerServiceTestUtils.startBackupThread(null),
- new File(application.getCacheDir(), "base_state"),
- new File(application.getCacheDir(), "data"),
- mTransportManager);
+ BackupManagerServiceTestUtils.startBackupThread(null));
mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
}
/**
+ * Clean up and reset state that was created for testing {@link BackupManagerService}
+ * operations.
+ */
+ @After
+ public void tearDown() throws Exception {
+ ShadowBinder.reset();
+ }
+
+ /**
* Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
* This is specifically to prevent overloading the logs in production.
*/
@@ -278,11 +298,41 @@
// ---------------------------------------------
// Settings tests
// ---------------------------------------------
+ /**
+ * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void setBackupEnabled_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.setBackupEnabled(mUserId, true));
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not
+ * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is
+ * the same as the target user id.
+ */
+ @Test
+ public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
+ ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.setBackupEnabled(mUserId, true);
+
+ verify(mUserBackupManagerService).setBackupEnabled(true);
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
- mBackupManagerService.setBackupEnabled(true);
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.setBackupEnabled(mUserId, true);
verify(mUserBackupManagerService).setBackupEnabled(true);
}
@@ -303,10 +353,25 @@
verify(mUserBackupManagerService).setBackupProvisioned(true);
}
+ /**
+ * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.isBackupEnabled(mUserId));
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
- mBackupManagerService.isBackupEnabled();
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.isBackupEnabled(mUserId);
verify(mUserBackupManagerService).isBackupEnabled();
}
@@ -334,30 +399,81 @@
verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
}
+ /**
+ * Test verifying that {@link BackupManagerService#backupNow(int)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testBackupNow_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.backupNow(mUserId));
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testBackupNow_callsBackupNowForUser() throws Exception {
- mBackupManagerService.backupNow();
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.backupNow(mUserId);
verify(mUserBackupManagerService).backupNow();
}
- /** Test that the backup service routes methods correctly to the user that requests it. */
+ /**
+ * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver,
+ * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission.
+ */
@Test
- public void testRequestBackup_callsRequestBackupForUser() throws Exception {
+ public void testRequestBackup_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
String[] packages = {TEST_PACKAGE};
IBackupObserver observer = mock(IBackupObserver.class);
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
- mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0);
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0));
+ }
+
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_callsRequestBackupForUser() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+ mBackupManagerService.requestBackup(mUserId, packages, observer, monitor,
+ /* flags */ 0);
verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
}
+ /**
+ * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testCancelBackups_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.cancelBackups(mUserId));
+ }
+
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
- mBackupManagerService.cancelBackups();
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.cancelBackups(mUserId);
verify(mUserBackupManagerService).cancelBackups();
}
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 9d43819..efbcb96 100644
--- a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -148,7 +148,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
.thenReturn("destinationString");
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
String destination = backupManagerService.getDestinationString(mTransportName);
@@ -164,7 +164,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
.thenThrow(TransportNotRegisteredException.class);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
String destination = backupManagerService.getDestinationString(mTransportName);
@@ -180,7 +180,7 @@
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
.thenThrow(TransportNotRegisteredException.class);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
SecurityException.class,
@@ -195,7 +195,7 @@
public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
@@ -211,7 +211,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
@@ -229,7 +229,7 @@
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport);
ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
SecurityException.class,
@@ -246,7 +246,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
String[] filtered =
backupManagerService.filterAppsEligibleForBackup(
@@ -264,7 +264,7 @@
@Test
public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
String[] filtered =
backupManagerService.filterAppsEligibleForBackup(
@@ -281,7 +281,7 @@
public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
SecurityException.class,
@@ -319,7 +319,7 @@
public void testSelectBackupTransport() throws Exception {
setUpForSelectTransport();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
String oldTransport =
backupManagerService.selectBackupTransport(mNewTransport.transportName);
@@ -338,7 +338,7 @@
public void testSelectBackupTransport_withoutPermission() throws Exception {
setUpForSelectTransport();
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
SecurityException.class,
@@ -356,7 +356,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
.thenReturn(BackupManager.SUCCESS);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
@@ -380,7 +380,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
.thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
@@ -402,7 +402,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
.thenReturn(BackupManager.SUCCESS);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
@@ -421,7 +421,7 @@
public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
setUpForSelectTransport();
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
ComponentName newTransportComponent = mNewTransport.getTransportComponent();
expectThrows(
@@ -445,7 +445,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.getCurrentTransportComponent())
.thenReturn(mTransport.getTransportComponent());
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
@@ -460,7 +460,7 @@
public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
@@ -476,7 +476,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.getCurrentTransportComponent())
.thenThrow(TransportNotRegisteredException.class);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
@@ -490,7 +490,7 @@
@Test
public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
}
@@ -525,7 +525,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.updateTransportAttributes(
mTransportUid,
@@ -556,7 +556,7 @@
throws Exception {
setUpForUpdateTransportAttributes();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
SecurityException.class,
@@ -581,7 +581,7 @@
throws Exception {
setUpForUpdateTransportAttributes();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
RuntimeException.class,
@@ -605,7 +605,7 @@
public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
setUpForUpdateTransportAttributes();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
RuntimeException.class,
@@ -630,7 +630,7 @@
throws Exception {
setUpForUpdateTransportAttributes();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
RuntimeException.class,
@@ -657,7 +657,7 @@
throws Exception {
setUpForUpdateTransportAttributes();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
RuntimeException.class,
@@ -696,7 +696,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.updateTransportAttributes(
mTransportUid,
@@ -727,7 +727,7 @@
throws Exception {
setUpForUpdateTransportAttributes();
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
SecurityException.class,
@@ -766,7 +766,7 @@
@Test
public void testRequestBackup_whenPermissionDenied() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
SecurityException.class,
@@ -780,7 +780,7 @@
@Test
public void testRequestBackup_whenPackagesNull() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
IllegalArgumentException.class,
@@ -796,7 +796,7 @@
@Test
public void testRequestBackup_whenPackagesEmpty() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
IllegalArgumentException.class,
@@ -811,7 +811,7 @@
@Test
public void testRequestBackup_whenBackupDisabled() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(false);
int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -828,7 +828,7 @@
@Test
public void testRequestBackup_whenNotProvisioned() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setProvisioned(false);
int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -846,7 +846,7 @@
public void testRequestBackup_whenTransportNotRegistered() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport.unregistered());
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(true);
backupManagerService.setProvisioned(true);
@@ -866,7 +866,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
mShadowPackageManager.addPackage(PACKAGE_1);
setUpCurrentTransport(mTransportManager, mTransport);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(true);
backupManagerService.setProvisioned(true);
// Haven't set PACKAGE_1 as eligible
@@ -934,7 +934,7 @@
@Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
public void testBackupNow_clearsCallingIdentityForJobScheduler() {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
setUpPowerManager(backupManagerService);
ShadowBinder.setCallingUid(1);
@@ -952,7 +952,7 @@
@Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
setUpPowerManager(backupManagerService);
ShadowBinder.setCallingUid(1);
@@ -963,54 +963,170 @@
}
private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
- UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(true);
backupManagerService.setProvisioned(true);
return backupManagerService;
}
/**
- * Test verifying that {@link UserBackupManagerService#UserBackupManagerService(Context,
+ * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
* Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
- * to the backup handler thread.
+ * to the backup thread.
*/
@Test
- public void testConstructor_postRegisterTransports() {
+ public void testCreateAndInitializeService_postRegisterTransports() {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- createBackupManagerService();
-
- mShadowBackupLooper.runToEndOfTasks();
- verify(mTransportManager).registerTransports();
- }
-
- /**
- * Test verifying that the {@link UserBackupManagerService#UserBackupManagerService(Context,
- * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
- * transports in its own thread.
- */
- @Test
- public void testConstructor_doesNotRegisterTransportsSynchronously() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
- createBackupManagerService();
-
- // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
- verify(mTransportManager, never()).registerTransports();
- }
-
- private UserBackupManagerService createBackupManagerService() {
- return new UserBackupManagerService(
+ UserBackupManagerService.createAndInitializeService(
mContext,
new Trampoline(mContext),
mBackupThread,
mBaseStateDir,
mDataDir,
mTransportManager);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ verify(mTransportManager).registerTransports();
}
- private UserBackupManagerService createInitializedBackupManagerService() {
- return BackupManagerServiceTestUtils.createInitializedUserBackupManagerService(
+ /**
+ * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
+ * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
+ * transports on the main thread.
+ */
+ @Test
+ public void testCreateAndInitializeService_doesNotRegisterTransportsSynchronously() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+ UserBackupManagerService.createAndInitializeService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+
+ // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
+ verify(mTransportManager, never()).registerTransports();
+ }
+
+ /**
+ * Test checking non-null argument on {@link
+ * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+ * File, TransportManager)}.
+ */
+ @Test
+ public void testCreateAndInitializeService_withNullContext_throws() {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ UserBackupManagerService.createAndInitializeService(
+ /* context */ null,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager));
+ }
+
+ /**
+ * Test checking non-null argument on {@link
+ * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+ * File, TransportManager)}.
+ */
+ @Test
+ public void testCreateAndInitializeService_withNullTrampoline_throws() {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ UserBackupManagerService.createAndInitializeService(
+ mContext,
+ /* trampoline */ null,
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager));
+ }
+
+ /**
+ * Test checking non-null argument on {@link
+ * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+ * File, TransportManager)}.
+ */
+ @Test
+ public void testCreateAndInitializeService_withNullBackupThread_throws() {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ UserBackupManagerService.createAndInitializeService(
+ mContext,
+ new Trampoline(mContext),
+ /* backupThread */ null,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager));
+ }
+
+ /**
+ * Test checking non-null argument on {@link
+ * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+ * File, TransportManager)}.
+ */
+ @Test
+ public void testCreateAndInitializeService_withNullStateDir_throws() {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ UserBackupManagerService.createAndInitializeService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ /* baseStateDir */ null,
+ mDataDir,
+ mTransportManager));
+ }
+
+ /**
+ * Test checking non-null argument on {@link
+ * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+ * File, TransportManager)}.
+ */
+ @Test
+ public void testCreateAndInitializeService_withNullDataDir_throws() {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ UserBackupManagerService.createAndInitializeService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ /* dataDir */ null,
+ mTransportManager));
+ }
+
+ /**
+ * Test checking non-null argument on {@link
+ * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+ * File, TransportManager)}.
+ */
+ @Test
+ public void testCreateAndInitializeService_withNullTransportManager_throws() {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ UserBackupManagerService.createAndInitializeService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ /* transportManager */ null));
+ }
+
+ private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
+ return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
new file mode 100644
index 0000000..5342efa
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.security.SecureRandom;
+import java.util.Optional;
+
+/** Tests for {@link RecoverableKeyStoreSecondaryKeyManager}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
+public class RecoverableKeyStoreSecondaryKeyManagerTest {
+ private static final String BACKUP_KEY_ALIAS_PREFIX =
+ "com.android.server.backup/recoverablekeystore/";
+ private static final int BITS_PER_BYTE = 8;
+ private static final int BACKUP_KEY_SUFFIX_LENGTH_BYTES = 128 / BITS_PER_BYTE;
+ private static final int HEX_PER_BYTE = 2;
+ private static final int BACKUP_KEY_ALIAS_LENGTH =
+ BACKUP_KEY_ALIAS_PREFIX.length() + BACKUP_KEY_SUFFIX_LENGTH_BYTES * HEX_PER_BYTE;
+ private static final String NONEXISTENT_KEY_ALIAS = "NONEXISTENT_KEY_ALIAS";
+
+ private RecoverableKeyStoreSecondaryKeyManager mRecoverableKeyStoreSecondaryKeyManager;
+ private Context mContext;
+
+ /** Create a new {@link RecoverableKeyStoreSecondaryKeyManager} to use in tests. */
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+
+ mRecoverableKeyStoreSecondaryKeyManager =
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(mContext), new SecureRandom());
+ }
+
+ /** Reset the {@link ShadowRecoveryController}. */
+ @After
+ public void tearDown() throws Exception {
+ ShadowRecoveryController.reset();
+ }
+
+ /** The generated key should always have the prefix {@code BACKUP_KEY_ALIAS_PREFIX}. */
+ @Test
+ public void generate_generatesKeyWithExpectedPrefix() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ assertThat(key.getAlias()).startsWith(BACKUP_KEY_ALIAS_PREFIX);
+ }
+
+ /** The generated key should always have length {@code BACKUP_KEY_ALIAS_LENGTH}. */
+ @Test
+ public void generate_generatesKeyWithExpectedLength() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ assertThat(key.getAlias()).hasLength(BACKUP_KEY_ALIAS_LENGTH);
+ }
+
+ /** Ensure that hidden API exceptions are rethrown when generating keys. */
+ @Test
+ public void generate_encounteringHiddenApiException_rethrowsException() {
+ ShadowRecoveryController.setThrowsInternalError(true);
+
+ assertThrows(
+ InternalRecoveryServiceException.class,
+ mRecoverableKeyStoreSecondaryKeyManager::generate);
+ }
+
+ /** Ensure that retrieved keys correspond to those generated earlier. */
+ @Test
+ public void get_getsKeyGeneratedByController() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
+ mRecoverableKeyStoreSecondaryKeyManager.get(key.getAlias());
+
+ assertThat(retrievedKey.isPresent()).isTrue();
+ assertThat(retrievedKey.get().getAlias()).isEqualTo(key.getAlias());
+ assertThat(retrievedKey.get().getSecretKey()).isEqualTo(key.getSecretKey());
+ }
+
+ /**
+ * Ensure that a call to {@link RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)}
+ * for nonexistent aliases returns an emtpy {@link Optional}.
+ */
+ @Test
+ public void get_forNonExistentKey_returnsEmptyOptional() throws Exception {
+ Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
+ mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
+
+ assertThat(retrievedKey.isPresent()).isFalse();
+ }
+
+ /**
+ * Ensure that exceptions occurring during {@link
+ * RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} are not rethrown.
+ */
+ @Test
+ public void get_encounteringInternalException_doesNotPropagateException() throws Exception {
+ ShadowRecoveryController.setThrowsInternalError(true);
+
+ // Should not throw exception
+ mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
+ }
+
+ /** Ensure that keys are correctly removed from the store. */
+ @Test
+ public void remove_removesKeyFromRecoverableStore() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ mRecoverableKeyStoreSecondaryKeyManager.remove(key.getAlias());
+
+ assertThat(RecoveryController.getInstance(mContext).getAliases())
+ .doesNotContain(key.getAlias());
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
new file mode 100644
index 0000000..89977f8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey.Status;
+import com.android.server.backup.testing.CryptoTestUtils;
+import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import javax.crypto.SecretKey;
+
+/** Tests for {@link RecoverableKeyStoreSecondaryKey}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
+public class RecoverableKeyStoreSecondaryKeyTest {
+ private static final String TEST_ALIAS = "test";
+ private static final int NONEXISTENT_STATUS_CODE = 42;
+
+ private RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ private SecretKey mGeneratedSecretKey;
+ private Context mContext;
+
+ /** Instantiate a {@link RecoverableKeyStoreSecondaryKey} to use in tests. */
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+ mGeneratedSecretKey = CryptoTestUtils.generateAesKey();
+ mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, mGeneratedSecretKey);
+ }
+
+ /** Reset the {@link ShadowRecoveryController}. */
+ @After
+ public void tearDown() throws Exception {
+ ShadowRecoveryController.reset();
+ }
+
+ /**
+ * Checks that {@link RecoverableKeyStoreSecondaryKey#getAlias()} returns the value supplied in
+ * the constructor.
+ */
+ @Test
+ public void getAlias() {
+ String alias = mSecondaryKey.getAlias();
+
+ assertThat(alias).isEqualTo(TEST_ALIAS);
+ }
+
+ /**
+ * Checks that {@link RecoverableKeyStoreSecondaryKey#getSecretKey()} returns the value supplied
+ * in the constructor.
+ */
+ @Test
+ public void getSecretKey() {
+ SecretKey secretKey = mSecondaryKey.getSecretKey();
+
+ assertThat(secretKey).isEqualTo(mGeneratedSecretKey);
+ }
+
+ /**
+ * Checks that passing a secret key that is null to the constructor throws an exception.
+ */
+ @Test
+ public void constructor_withNullSecretKey_throwsNullPointerException() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, null));
+ }
+
+ /**
+ * Checks that passing an alias that is null to the constructor throws an exception.
+ */
+ @Test
+ public void constructor_withNullAlias_throwsNullPointerException() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new RecoverableKeyStoreSecondaryKey(null, mGeneratedSecretKey));
+ }
+
+ /** Checks that the synced status is returned correctly. */
+ @Test
+ public void getStatus_whenSynced_returnsSynced() throws Exception {
+ setStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.SYNCED);
+ }
+
+ /** Checks that the in progress sync status is returned correctly. */
+ @Test
+ public void getStatus_whenNotSynced_returnsNotSynced() throws Exception {
+ setStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.NOT_SYNCED);
+ }
+
+ /** Checks that the failure status is returned correctly. */
+ @Test
+ public void getStatus_onPermanentFailure_returnsDestroyed() throws Exception {
+ setStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.DESTROYED);
+ }
+
+ /** Checks that an unknown status results in {@code NOT_SYNCED} being returned. */
+ @Test
+ public void getStatus_forUnknownStatusCode_returnsNotSynced() throws Exception {
+ setStatus(NONEXISTENT_STATUS_CODE);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.NOT_SYNCED);
+ }
+
+ /** Checks that an internal error results in {@code NOT_SYNCED} being returned. */
+ @Test
+ public void getStatus_onInternalError_returnsNotSynced() throws Exception {
+ ShadowRecoveryController.setThrowsInternalError(true);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.NOT_SYNCED);
+ }
+
+ private void setStatus(int status) throws Exception {
+ ShadowRecoveryController.setRecoveryStatus(TEST_ALIAS, status);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
new file mode 100644
index 0000000..48216f8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.security.SecureRandom;
+
+import javax.crypto.SecretKey;
+
+/** Tests for {@link TertiaryKeyGenerator}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class TertiaryKeyGeneratorTest {
+ private static final String KEY_ALGORITHM = "AES";
+ private static final int KEY_SIZE_BITS = 256;
+
+ private TertiaryKeyGenerator mTertiaryKeyGenerator;
+
+ /** Instantiate a new {@link TertiaryKeyGenerator} for use in tests. */
+ @Before
+ public void setUp() {
+ mTertiaryKeyGenerator = new TertiaryKeyGenerator(new SecureRandom());
+ }
+
+ /** Generated keys should be AES keys. */
+ @Test
+ public void generate_generatesAESKeys() {
+ SecretKey secretKey = mTertiaryKeyGenerator.generate();
+
+ assertThat(secretKey.getAlgorithm()).isEqualTo(KEY_ALGORITHM);
+ }
+
+ /** Generated keys should be 256 bits in size. */
+ @Test
+ public void generate_generates256BitKeys() {
+ SecretKey secretKey = mTertiaryKeyGenerator.generate();
+
+ assertThat(secretKey.getEncoded()).hasLength(KEY_SIZE_BITS / 8);
+ }
+
+ /**
+ * Subsequent calls to {@link TertiaryKeyGenerator#generate()} should generate different keys.
+ */
+ @Test
+ public void generate_generatesNewKeys() {
+ SecretKey key1 = mTertiaryKeyGenerator.generate();
+ SecretKey key2 = mTertiaryKeyGenerator.generate();
+
+ assertThat(key1).isNotEqualTo(key2);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
new file mode 100644
index 0000000..49bb410
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+/** Tests for {@link TertiaryKeyRotationTracker}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class TertiaryKeyRotationTrackerTest {
+ private static final String PACKAGE_1 = "com.package.one";
+ private static final int NUMBER_OF_BACKUPS_BEFORE_ROTATION = 31;
+
+ private TertiaryKeyRotationTracker mTertiaryKeyRotationTracker;
+
+ /** Instantiate a {@link TertiaryKeyRotationTracker} for use in tests. */
+ @Before
+ public void setUp() {
+ mTertiaryKeyRotationTracker = newInstance();
+ }
+
+ /** New packages should not be due for key rotation. */
+ @Test
+ public void isKeyRotationDue_forNewPackage_isFalse() {
+ // Simulate a new package by not calling simulateBackups(). As a result, PACKAGE_1 hasn't
+ // been seen by mTertiaryKeyRotationTracker before.
+ boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDue).isFalse();
+ }
+
+ /**
+ * Key rotation should not be due after less than {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION}
+ * backups.
+ */
+ @Test
+ public void isKeyRotationDue_afterLessThanRotationAmountBackups_isFalse() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION - 1);
+
+ boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDue).isFalse();
+ }
+
+ /** Key rotation should be due after {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} backups. */
+ @Test
+ public void isKeyRotationDue_afterRotationAmountBackups_isTrue() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+ boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDue).isTrue();
+ }
+
+ /**
+ * A call to {@link TertiaryKeyRotationTracker#resetCountdown(String)} should make sure no key
+ * rotation is due.
+ */
+ @Test
+ public void resetCountdown_makesKeyRotationNotDue() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+ mTertiaryKeyRotationTracker.resetCountdown(PACKAGE_1);
+
+ assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ /**
+ * New instances of {@link TertiaryKeyRotationTracker} should read state about the number of
+ * backups from disk.
+ */
+ @Test
+ public void isKeyRotationDue_forNewInstance_readsStateFromDisk() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+ boolean keyRotationDueForNewInstance = newInstance().isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDueForNewInstance).isTrue();
+ }
+
+ /**
+ * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should mark all previously
+ * seen packages for rotation.
+ */
+ @Test
+ public void markAllForRotation_marksSeenPackagesForKeyRotation() {
+ simulateBackups(PACKAGE_1, /*numberOfBackups=*/ 1);
+
+ mTertiaryKeyRotationTracker.markAllForRotation();
+
+ assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isTrue();
+ }
+
+ /**
+ * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should not mark any new
+ * packages for rotation.
+ */
+ @Test
+ public void markAllForRotation_doesNotMarkUnseenPackages() {
+ mTertiaryKeyRotationTracker.markAllForRotation();
+
+ assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ private void simulateBackups(String packageName, int numberOfBackups) {
+ while (numberOfBackups > 0) {
+ mTertiaryKeyRotationTracker.recordBackup(packageName);
+ numberOfBackups--;
+ }
+ }
+
+ private static TertiaryKeyRotationTracker newInstance() {
+ return TertiaryKeyRotationTracker.getInstance(RuntimeEnvironment.application);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 1aa4999..099127c 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -25,12 +25,9 @@
import static android.app.backup.ForwardingBackupAgent.forward;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils
- .createInitializedUserBackupManagerService;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils
- .setUpBackupManagerServiceBasics;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils
- .setUpBinderCallerAndApplicationAsSystem;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBackupManagerServiceBasics;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBinderCallerAndApplicationAsSystem;
import static com.android.server.backup.testing.PackageData.PM_PACKAGE;
import static com.android.server.backup.testing.PackageData.fullBackupPackage;
import static com.android.server.backup.testing.PackageData.keyValuePackage;
@@ -226,9 +223,8 @@
// Needed to be able to use a real BMS instead of a mock
setUpBinderCallerAndApplicationAsSystem(mApplication);
mBackupManagerService =
- spy(
- createInitializedUserBackupManagerService(
- mContext, mBaseStateDir, mDataDir, mTransportManager));
+ spy(createUserBackupManagerServiceAndRunTasks(
+ mContext, mBaseStateDir, mDataDir, mTransportManager));
setUpBackupManagerServiceBasics(
mBackupManagerService,
mApplication,
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index bacc44e..06f6d21 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -52,23 +52,36 @@
/** Test utils for {@link UserBackupManagerService} and friends. */
public class BackupManagerServiceTestUtils {
/**
- * If the class-under-test is going to execute methods as the system, it's a good idea to also
- * call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
+ * Creates an instance of {@link UserBackupManagerService} with a new backup thread and runs
+ * tasks that were posted to it during instantiation.
+ *
+ * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
+ * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
+ *
+ * @see #createUserBackupManagerServiceAndRunTasks(Context, HandlerThread, File, File,
+ * TransportManager)
*/
- public static UserBackupManagerService createInitializedUserBackupManagerService(
+ public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
- return createInitializedUserBackupManagerService(
+ return createUserBackupManagerServiceAndRunTasks(
context, startBackupThread(null), baseStateDir, dataDir, transportManager);
}
- public static UserBackupManagerService createInitializedUserBackupManagerService(
+ /**
+ * Creates an instance of {@link UserBackupManagerService} with the supplied backup thread
+ * {@code backupThread} and runs tasks that were posted to it during instantiation.
+ *
+ * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
+ * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
+ */
+ public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
Context context,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
UserBackupManagerService backupManagerService =
- new UserBackupManagerService(
+ UserBackupManagerService.createAndInitializeService(
context,
new Trampoline(context),
backupThread,
diff --git a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java b/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
index beb5941..30c7336 100644
--- a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
+++ b/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
@@ -7,7 +7,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.os.Looper;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -44,7 +43,7 @@
when(mMockNative.pauseGeofence(anyInt())).thenReturn(true);
when(mMockNative.removeGeofence(anyInt())).thenReturn(true);
when(mMockNative.resumeGeofence(anyInt(), anyInt())).thenReturn(true);
- mTestProvider = new GnssGeofenceProvider(Looper.myLooper(), mMockNative);
+ mTestProvider = new GnssGeofenceProvider(mMockNative);
mTestProvider.addCircularHardwareGeofence(GEOFENCE_ID, LATITUDE,
LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
NOTIFICATION_RESPONSIVENESS,
diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
index 59e9a15..aa2a96e 100644
--- a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
+++ b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
@@ -16,6 +16,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
/**
* Unit tests for {@link GnssNavigationMessageProvider}.
@@ -33,8 +34,8 @@
when(mMockNative.startNavigationMessageCollection()).thenReturn(true);
when(mMockNative.stopNavigationMessageCollection()).thenReturn(true);
- mTestProvider = new GnssNavigationMessageProvider(new Handler(Looper.myLooper()),
- mMockNative) {
+ mTestProvider = new GnssNavigationMessageProvider(RuntimeEnvironment.application,
+ new Handler(Looper.myLooper()), mMockNative) {
@Override
public boolean isGpsEnabled() {
return true;
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java
index 1ece49e..9de9f50 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java
@@ -17,9 +17,11 @@
package com.android.server.testing.shadows;
import android.os.Binder;
+import android.os.UserHandle;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
/**
* Extends {@link org.robolectric.shadows.ShadowBinder} with {@link Binder#clearCallingIdentity()}
@@ -30,6 +32,7 @@
public class ShadowBinder extends org.robolectric.shadows.ShadowBinder {
public static final Integer LOCAL_UID = 1000;
private static Integer originalCallingUid;
+ private static UserHandle sCallingUserHandle;
@Implementation
protected static long clearCallingIdentity() {
@@ -42,4 +45,30 @@
protected static void restoreCallingIdentity(long token) {
setCallingUid(originalCallingUid);
}
+
+ public static void setCallingUserHandle(UserHandle userHandle) {
+ sCallingUserHandle = userHandle;
+ }
+
+ /**
+ * Shadows {@link Binder#getCallingUserHandle()}. If {@link ShadowBinder#sCallingUserHandle}
+ * is set, return that; otherwise mimic the default implementation.
+ */
+ @Implementation
+ public static UserHandle getCallingUserHandle() {
+ if (sCallingUserHandle != null) {
+ return sCallingUserHandle;
+ } else {
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
+ }
+ }
+
+ /**
+ * Clean up and reset state that was created for testing.
+ */
+ @Resetter
+ public static void reset() {
+ sCallingUserHandle = null;
+ org.robolectric.shadows.ShadowBinder.reset();
+ }
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
new file mode 100644
index 0000000..9c06d81
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.shadows;
+
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/** Shadow {@link InternalRecoveryServiceException}. */
+@Implements(InternalRecoveryServiceException.class)
+public class ShadowInternalRecoveryServiceException {
+ private String mMessage;
+
+ @Implementation
+ public void __constructor__(String message) {
+ mMessage = message;
+ }
+
+ @Implementation
+ public void __constructor__(String message, Throwable cause) {
+ mMessage = message;
+ }
+
+ @Implementation
+ public String getMessage() {
+ return mMessage;
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java
new file mode 100644
index 0000000..7dad8a4
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.shadows;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.google.common.collect.ImmutableList;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.lang.reflect.Constructor;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.crypto.KeyGenerator;
+
+/**
+ * Shadow of {@link RecoveryController}.
+ *
+ * <p>Instead of generating keys via the {@link RecoveryController}, this shadow generates them in
+ * memory.
+ */
+@Implements(RecoveryController.class)
+public class ShadowRecoveryController {
+ private static final String KEY_GENERATOR_ALGORITHM = "AES";
+ private static final int KEY_SIZE_BITS = 256;
+
+ private static boolean sIsSupported = true;
+ private static boolean sThrowsInternalError = false;
+ private static HashMap<String, Key> sKeysByAlias = new HashMap<>();
+ private static HashMap<String, Integer> sKeyStatusesByAlias = new HashMap<>();
+
+ @Implementation
+ public void __constructor__() {
+ // do not throw
+ }
+
+ @Implementation
+ public static RecoveryController getInstance(Context context) {
+ // Call non-public constructor.
+ try {
+ Constructor<RecoveryController> constructor = RecoveryController.class.getConstructor();
+ return constructor.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Implementation
+ public static boolean isRecoverableKeyStoreEnabled(Context context) {
+ return sIsSupported;
+ }
+
+ @Implementation
+ public Key generateKey(String alias)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ maybeThrowError();
+ KeyGenerator keyGenerator;
+ try {
+ keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
+ } catch (NoSuchAlgorithmException e) {
+ // Should never happen
+ throw new RuntimeException(e);
+ }
+
+ keyGenerator.init(KEY_SIZE_BITS);
+ Key key = keyGenerator.generateKey();
+ sKeysByAlias.put(alias, key);
+ sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ return key;
+ }
+
+ @Implementation
+ public Key getKey(String alias)
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ return sKeysByAlias.get(alias);
+ }
+
+ @Implementation
+ public void removeKey(String alias) throws InternalRecoveryServiceException {
+ sKeyStatusesByAlias.remove(alias);
+ sKeysByAlias.remove(alias);
+ }
+
+ @Implementation
+ public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
+ maybeThrowError();
+ return sKeyStatusesByAlias.getOrDefault(
+ alias, RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+ }
+
+ @Implementation
+ public List<String> getAliases() throws InternalRecoveryServiceException {
+ return ImmutableList.copyOf(sKeyStatusesByAlias.keySet());
+ }
+
+ private static void maybeThrowError() throws InternalRecoveryServiceException {
+ if (sThrowsInternalError) {
+ throw new InternalRecoveryServiceException("test error");
+ }
+ }
+
+ /** Sets the recovery status of the key with {@code alias} to {@code status}. */
+ public static void setRecoveryStatus(String alias, int status) {
+ sKeyStatusesByAlias.put(alias, status);
+ }
+
+ /** Sets all existing keys to being synced. */
+ public static void syncAllKeys() {
+ for (String alias : sKeysByAlias.keySet()) {
+ sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNCED);
+ }
+ }
+
+ public static void setThrowsInternalError(boolean throwsInternalError) {
+ ShadowRecoveryController.sThrowsInternalError = throwsInternalError;
+ }
+
+ public static void setIsSupported(boolean isSupported) {
+ ShadowRecoveryController.sIsSupported = isSupported;
+ }
+
+ @Resetter
+ public static void reset() {
+ sIsSupported = true;
+ sThrowsInternalError = false;
+ sKeysByAlias.clear();
+ sKeyStatusesByAlias.clear();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
new file mode 100644
index 0000000..8e78a56
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkPolicyManager;
+import android.os.Build;
+import android.os.SystemClock;
+import android.util.DataUnit;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.net.NetworkPolicyManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ConnectivityControllerTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private ConnectivityManager mConnManager;
+ @Mock
+ private NetworkPolicyManager mNetPolicyManager;
+ @Mock
+ private NetworkPolicyManagerInternal mNetPolicyManagerInternal;
+ @Mock
+ private JobSchedulerService mService;
+
+ private Constants mConstants;
+
+ private static final int UID_RED = 10001;
+ private static final int UID_BLUE = 10002;
+
+ @Before
+ public void setUp() throws Exception {
+ // Assume all packages are current SDK
+ final PackageManagerInternal pm = mock(PackageManagerInternal.class);
+ when(pm.getPackageTargetSdkVersion(anyString()))
+ .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, pm);
+
+ LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
+ LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
+
+ // Freeze the clocks at this moment in time
+ JobSchedulerService.sSystemClock =
+ Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sUptimeMillisClock =
+ Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+ // Assume default constants for now
+ mConstants = new Constants();
+
+ // Get our mocks ready
+ when(mContext.getSystemServiceName(ConnectivityManager.class))
+ .thenReturn(Context.CONNECTIVITY_SERVICE);
+ when(mContext.getSystemService(ConnectivityManager.class))
+ .thenReturn(mConnManager);
+ when(mContext.getSystemServiceName(NetworkPolicyManager.class))
+ .thenReturn(Context.NETWORK_POLICY_SERVICE);
+ when(mContext.getSystemService(NetworkPolicyManager.class))
+ .thenReturn(mNetPolicyManager);
+ when(mService.getTestableContext()).thenReturn(mContext);
+ when(mService.getLock()).thenReturn(mService);
+ when(mService.getConstants()).thenReturn(mConstants);
+ }
+
+ @Test
+ public void testInsane() throws Exception {
+ final Network net = new Network(101);
+ final JobInfo.Builder job = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+
+ // Slow network is too slow
+ assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
+ createCapabilities().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1), mConstants));
+ // Fast network looks great
+ assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net,
+ createCapabilities().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1024), mConstants));
+ }
+
+ @Test
+ public void testCongestion() throws Exception {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final JobInfo.Builder job = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
+ final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
+
+ // Uncongested network is whenever
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ }
+
+ // Congested network is more selective
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities();
+ assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ }
+ }
+
+ @Test
+ public void testRelaxed() throws Exception {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final JobInfo.Builder job = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
+ final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
+ final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
+
+ job.setIsPrefetch(true);
+ final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
+ final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
+
+ // Unmetered network is whenever
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+ assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
+ }
+
+ // Metered network is only when prefetching and late
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertFalse(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
+ }
+ }
+
+ @Test
+ public void testUpdates() throws Exception {
+ final ArgumentCaptor<NetworkCallback> callback = ArgumentCaptor
+ .forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
+
+ final ConnectivityController controller = new ConnectivityController(mService);
+
+ final Network meteredNet = new Network(101);
+ final NetworkCapabilities meteredCaps = createCapabilities();
+ final Network unmeteredNet = new Network(202);
+ final NetworkCapabilities unmeteredCaps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ // Pretend we're offline when job is added
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, null, null);
+ answerNetwork(UID_BLUE, null, null);
+
+ controller.maybeStartTrackingJobLocked(red, null);
+ controller.maybeStartTrackingJobLocked(blue, null);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Metered network
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, meteredNet, meteredCaps);
+ answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+
+ callback.getValue().onCapabilitiesChanged(meteredNet, meteredCaps);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Unmetered network background
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, meteredNet, meteredCaps);
+ answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+
+ callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Lost metered network
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, unmeteredNet, unmeteredCaps);
+ answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+
+ callback.getValue().onLost(meteredNet);
+
+ assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Specific UID was blocked
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, null, null);
+ answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+
+ callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+ }
+
+ @Test
+ public void testRequestStandbyExceptionLocked() {
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+
+ controller.requestStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ // Whitelisting doesn't need to be requested again.
+ controller.requestStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+
+ controller.requestStandbyExceptionLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ }
+
+ @Test
+ public void testWouldBeReadyWithConnectivityLocked() {
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+
+ doReturn(false).when(controller).isNetworkAvailable(any());
+ assertFalse(controller.wouldBeReadyWithConnectivityLocked(red));
+
+ doReturn(true).when(controller).isNetworkAvailable(any());
+ doReturn(false).when(controller).wouldBeReadyWithConstraintLocked(any(),
+ eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(controller.wouldBeReadyWithConnectivityLocked(red));
+
+ doReturn(true).when(controller).isNetworkAvailable(any());
+ doReturn(true).when(controller).wouldBeReadyWithConstraintLocked(any(),
+ eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(controller.wouldBeReadyWithConnectivityLocked(red));
+ }
+
+ @Test
+ public void testEvaluateStateLocked_HeartbeatsOn() {
+ mConstants.USE_HEARTBEATS = true;
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+
+ controller.evaluateStateLocked(red);
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ }
+
+ @Test
+ public void testEvaluateStateLocked_JobWithoutConnectivity() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob().setMinimumLatency(1));
+
+ controller.evaluateStateLocked(red);
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ }
+
+ @Test
+ public void testEvaluateStateLocked_JobWouldBeReady() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+
+ controller.evaluateStateLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ // Whitelisting doesn't need to be requested again.
+ controller.evaluateStateLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+
+ controller.evaluateStateLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ }
+
+ @Test
+ public void testEvaluateStateLocked_JobWouldNotBeReady() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+
+ controller.evaluateStateLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+
+ // Test that a currently whitelisted uid is now removed.
+ controller.requestStandbyExceptionLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ controller.evaluateStateLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(false));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ }
+
+ @Test
+ public void testReevaluateStateLocked() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ final JobStatus redOne = createJobStatus(createJob(1)
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus redTwo = createJobStatus(createJob(2)
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+ controller.maybeStartTrackingJobLocked(redOne, null);
+ controller.maybeStartTrackingJobLocked(redTwo, null);
+ controller.maybeStartTrackingJobLocked(blue, null);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+ controller.requestStandbyExceptionLocked(redOne);
+ controller.requestStandbyExceptionLocked(redTwo);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+
+ // Make sure nothing happens if an exception hasn't been requested.
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ controller.reevaluateStateLocked(UID_BLUE);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+
+ // Make sure a job that isn't being tracked doesn't cause issues.
+ assertFalse(controller.isStandbyExceptionRequestedLocked(12345));
+ controller.reevaluateStateLocked(12345);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(12345), anyBoolean());
+
+ // Both jobs would still be ready. Exception should not be revoked.
+ doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ controller.reevaluateStateLocked(UID_RED);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+
+ // One job is still ready. Exception should not be revoked.
+ doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(eq(redOne));
+ doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(eq(redTwo));
+ controller.reevaluateStateLocked(UID_RED);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+
+ // Both jobs are not ready. Exception should be revoked.
+ doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ controller.reevaluateStateLocked(UID_RED);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(false));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ }
+
+ @Test
+ public void testMaybeRevokeStandbyExceptionLocked() {
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+ controller.requestStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+
+ // Try revoking for blue instead of red. Red should still have an exception requested.
+ controller.maybeRevokeStandbyExceptionLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(anyInt(), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+
+ // Now revoke for red.
+ controller.maybeRevokeStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(false));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ }
+
+ private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
+ when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
+ when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
+ if (net != null) {
+ final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
+ ni.setDetailedState(DetailedState.CONNECTED, null, null);
+ when(mConnManager.getNetworkInfoForUid(eq(net), eq(uid), anyBoolean())).thenReturn(ni);
+ }
+ }
+
+ private static NetworkCapabilities createCapabilities() {
+ return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_VALIDATED);
+ }
+
+ private static JobInfo.Builder createJob() {
+ return createJob(101);
+ }
+
+ private static JobInfo.Builder createJob(int jobId) {
+ return new JobInfo.Builder(jobId, new ComponentName("foo", "bar"));
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job) {
+ return createJobStatus(job, android.os.Process.NOBODY_UID, 0, Long.MAX_VALUE);
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job, int uid) {
+ return createJobStatus(job, uid, 0, Long.MAX_VALUE);
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job,
+ long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+ return createJobStatus(job, android.os.Process.NOBODY_UID,
+ earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
+ long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+ return new JobStatus(job.build(), uid, null, -1, 0, 0, null,
+ earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
new file mode 100644
index 0000000..71aec23
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -0,0 +1,980 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.job.JobInfo;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.controllers.QuotaController.TimingSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class QuotaControllerTest {
+ private static final long SECOND_IN_MILLIS = 1000L;
+ private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+ private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+ private static final String TAG_CLEANUP = "*job.cleanup*";
+ private static final String TAG_QUOTA_CHECK = "*job.quota_check*";
+ private static final int CALLING_UID = 1000;
+ private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+ private static final int SOURCE_USER_ID = 0;
+
+ private BroadcastReceiver mChargingReceiver;
+ private Constants mConstants;
+ private QuotaController mQuotaController;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private JobSchedulerService mJobSchedulerService;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManager;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ // Make sure constants turn on QuotaController.
+ mConstants = new Constants();
+ mConstants.USE_HEARTBEATS = false;
+
+ // Called in StateController constructor.
+ when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+ when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+ when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+ // Called in QuotaController constructor.
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+ doReturn(mock(BatteryManagerInternal.class))
+ .when(() -> LocalServices.getService(BatteryManagerInternal.class));
+ doReturn(mUsageStatsManager)
+ .when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
+ // Used in JobStatus.
+ doReturn(mock(PackageManagerInternal.class))
+ .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+ // Freeze the clocks at this moment in time
+ JobSchedulerService.sSystemClock =
+ Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sUptimeMillisClock =
+ Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+ // Initialize real objects.
+ // Capture the listeners.
+ ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ mQuotaController = new QuotaController(mJobSchedulerService);
+
+ verify(mContext).registerReceiver(receiverCaptor.capture(), any());
+ mChargingReceiver = receiverCaptor.getValue();
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private Clock getAdvancedClock(Clock clock, long incrementMs) {
+ return Clock.offset(clock, Duration.ofMillis(incrementMs));
+ }
+
+ private void advanceElapsedClock(long incrementMs) {
+ JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+ JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+ }
+
+ private void setCharging() {
+ Intent intent = new Intent(BatteryManager.ACTION_CHARGING);
+ mChargingReceiver.onReceive(mContext, intent);
+ }
+
+ private void setDischarging() {
+ Intent intent = new Intent(BatteryManager.ACTION_DISCHARGING);
+ mChargingReceiver.onReceive(mContext, intent);
+ }
+
+ private void setStandbyBucket(int bucketIndex) {
+ int bucket;
+ switch (bucketIndex) {
+ case ACTIVE_INDEX:
+ bucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+ break;
+ case WORKING_INDEX:
+ bucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+ break;
+ case FREQUENT_INDEX:
+ bucket = UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+ break;
+ case RARE_INDEX:
+ bucket = UsageStatsManager.STANDBY_BUCKET_RARE;
+ break;
+ default:
+ bucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
+ }
+ when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID),
+ anyLong())).thenReturn(bucket);
+ }
+
+ private void setStandbyBucket(int bucketIndex, JobStatus job) {
+ setStandbyBucket(bucketIndex);
+ job.setStandbyBucket(bucketIndex);
+ }
+
+ private JobStatus createJobStatus(String testTag, int jobId) {
+ JobInfo jobInfo = new JobInfo.Builder(jobId,
+ new ComponentName(mContext, "TestQuotaJobService"))
+ .setMinimumLatency(Math.abs(jobId) + 1)
+ .build();
+ return JobStatus.createFromJobInfo(
+ jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ }
+
+ private TimingSession createTimingSession(long start, long duration, int count) {
+ return new TimingSession(start, start + duration, count);
+ }
+
+ @Test
+ public void testSaveTimingSession() {
+ assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
+
+ List<TimingSession> expected = new ArrayList<>();
+ TimingSession one = new TimingSession(1, 10, 1);
+ TimingSession two = new TimingSession(11, 20, 2);
+ TimingSession thr = new TimingSession(21, 30, 3);
+
+ mQuotaController.saveTimingSession(0, "com.android.test", one);
+ expected.add(one);
+ assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+
+ mQuotaController.saveTimingSession(0, "com.android.test", two);
+ expected.add(two);
+ assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+
+ mQuotaController.saveTimingSession(0, "com.android.test", thr);
+ expected.add(thr);
+ assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+ }
+
+ @Test
+ public void testDeleteObsoleteSessionsLocked() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ TimingSession one = createTimingSession(
+ now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
+ TimingSession two = createTimingSession(
+ now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+ TimingSession thr = createTimingSession(
+ now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+ // Overlaps 24 hour boundary.
+ TimingSession fou = createTimingSession(
+ now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1);
+ // Way past the 24 hour boundary.
+ TimingSession fiv = createTimingSession(
+ now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4);
+ List<TimingSession> expected = new ArrayList<>();
+ // Added in correct (chronological) order.
+ expected.add(fou);
+ expected.add(thr);
+ expected.add(two);
+ expected.add(one);
+ mQuotaController.saveTimingSession(0, "com.android.test", fiv);
+ mQuotaController.saveTimingSession(0, "com.android.test", fou);
+ mQuotaController.saveTimingSession(0, "com.android.test", thr);
+ mQuotaController.saveTimingSession(0, "com.android.test", two);
+ mQuotaController.saveTimingSession(0, "com.android.test", one);
+
+ mQuotaController.deleteObsoleteSessionsLocked();
+
+ assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+ }
+
+ @Test
+ public void testGetTrailingExecutionTimeLocked_NoTimer() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Added in chronological order.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3));
+
+ assertEquals(0, mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ MINUTE_IN_MILLIS));
+ assertEquals(2 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ 3 * MINUTE_IN_MILLIS));
+ assertEquals(4 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ 5 * MINUTE_IN_MILLIS));
+ assertEquals(4 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ 49 * MINUTE_IN_MILLIS));
+ assertEquals(5 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ 50 * MINUTE_IN_MILLIS));
+ assertEquals(6 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ HOUR_IN_MILLIS));
+ assertEquals(11 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ 2 * HOUR_IN_MILLIS));
+ assertEquals(12 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ 3 * HOUR_IN_MILLIS));
+ assertEquals(22 * MINUTE_IN_MILLIS,
+ mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+ 6 * HOUR_IN_MILLIS));
+ }
+
+ @Test
+ public void testMaybeScheduleCleanupAlarmLocked() {
+ // No sessions saved yet.
+ mQuotaController.maybeScheduleCleanupAlarmLocked();
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any());
+
+ // Test with only one timing session saved.
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1));
+ mQuotaController.maybeScheduleCleanupAlarmLocked();
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+
+ // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleCleanupAlarmLocked();
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+ }
+
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_WorkingSet() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ // Working set window size is 2 hours.
+ final int standbyBucket = WORKING_INDEX;
+
+ // No sessions saved yet.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions out of window.
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions in window but still in quota.
+ final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
+ // Counting backwards, the quota will come back one minute before the end.
+ final long expectedAlarmTime =
+ end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Add some more sessions, but still in quota.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test when out of quota.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Alarm already scheduled, so make sure it's not scheduled again.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_Frequent() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ // Frequent window size is 8 hours.
+ final int standbyBucket = FREQUENT_INDEX;
+
+ // No sessions saved yet.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions out of window.
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions in window but still in quota.
+ final long start = now - (6 * HOUR_IN_MILLIS);
+ final long expectedAlarmTime =
+ start + 8 * HOUR_IN_MILLIS + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Add some more sessions, but still in quota.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test when out of quota.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Alarm already scheduled, so make sure it's not scheduled again.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_Rare() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ // Rare window size is 24 hours.
+ final int standbyBucket = RARE_INDEX;
+
+ // No sessions saved yet.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions out of window.
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions in window but still in quota.
+ final long start = now - (6 * HOUR_IN_MILLIS);
+ // Counting backwards, the first minute in the session is over the allowed time, so it
+ // needs to be excluded.
+ final long expectedAlarmTime =
+ start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Add some more sessions, but still in quota.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test when out of quota.
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1));
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Alarm already scheduled, so make sure it's not scheduled again.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+
+ /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_BucketChange() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+ // Affects rare bucket
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3));
+ // Affects frequent and rare buckets
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3));
+ // Affects working, frequent, and rare buckets
+ final long outOfQuotaTime = now - HOUR_IN_MILLIS;
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10));
+ // Affects all buckets
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3));
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ // Start in ACTIVE bucket.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+ any());
+ inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
+
+ // And down from there.
+ final long expectedWorkingAlarmTime =
+ outOfQuotaTime + (2 * HOUR_IN_MILLIS)
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ final long expectedFrequentAlarmTime =
+ outOfQuotaTime + (8 * HOUR_IN_MILLIS)
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ final long expectedRareAlarmTime =
+ outOfQuotaTime + (24 * HOUR_IN_MILLIS)
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // And back up again.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+ any());
+ inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class));
+ }
+
+ /** Tests that QuotaController doesn't throttle if throttling is turned off. */
+ @Test
+ public void testThrottleToggling() throws Exception {
+ setDischarging();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+ 10 * MINUTE_IN_MILLIS, 4));
+ JobStatus jobStatus = createJobStatus("testThrottleToggling", 1);
+ setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+ mConstants.USE_HEARTBEATS = true;
+ mQuotaController.onConstantsUpdatedLocked();
+ Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
+ assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+ mConstants.USE_HEARTBEATS = false;
+ mQuotaController.onConstantsUpdatedLocked();
+ Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
+ assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ }
+
+ @Test
+ public void testConstantsUpdating_ValidValues() {
+ mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
+
+ mQuotaController.onConstantsUpdatedLocked();
+
+ assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+ assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
+ assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
+ assertEquals(45 * MINUTE_IN_MILLIS,
+ mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
+ assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+ }
+
+ @Test
+ public void testConstantsUpdating_InvalidValues() {
+ // Test negatives
+ mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
+
+ mQuotaController.onConstantsUpdatedLocked();
+
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+ assertEquals(0, mQuotaController.getInQuotaBufferMs());
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+
+ // Test larger than a day. Controller should cap at one day.
+ mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
+
+ mQuotaController.onConstantsUpdatedLocked();
+
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+ assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+ }
+
+ /** Tests that TimingSessions aren't saved when the device is charging. */
+ @Test
+ public void testTimerTracking_Charging() {
+ setCharging();
+
+ JobStatus jobStatus = createJobStatus("testTimerTracking_Charging", 1);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /** Tests that TimingSessions are saved properly when the device is discharging. */
+ @Test
+ public void testTimerTracking_Discharging() {
+ setDischarging();
+
+ JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ List<TimingSession> expected = new ArrayList<>();
+
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Test overlapping jobs.
+ JobStatus jobStatus2 = createJobStatus("testTimerTracking_Discharging", 2);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+
+ JobStatus jobStatus3 = createJobStatus("testTimerTracking_Discharging", 3);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that TimingSessions are saved properly when the device alternates between
+ * charging and discharging.
+ */
+ @Test
+ public void testTimerTracking_ChargingAndDischarging() {
+ JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ JobStatus jobStatus3 = createJobStatus("testTimerTracking_ChargingAndDischarging", 3);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expected = new ArrayList<>();
+
+ // A job starting while charging. Only the portion that runs during the discharging period
+ // should be counted.
+ setCharging();
+
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ setDischarging();
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // One job starts while discharging, spans a charging session, and ends after the charging
+ // session. Only the portions during the discharging periods should be counted. This should
+ // result in two TimingSessions. A second job starts while discharging and ends within the
+ // charging session. Only the portion during the first discharging portion should be
+ // counted. A third job starts and ends within the charging session. The third job
+ // shouldn't be included in either job count.
+ setDischarging();
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ setCharging();
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ setDischarging();
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // A job starting while discharging and ending while charging. Only the portion that runs
+ // during the discharging period should be counted.
+ setDischarging();
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ setCharging();
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /** Tests that TimingSessions are saved properly when all the jobs are background jobs. */
+ @Test
+ public void testTimerTracking_AllBackground() {
+ setDischarging();
+
+ JobStatus jobStatus = createJobStatus("testTimerTracking_AllBackground", 1);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ List<TimingSession> expected = new ArrayList<>();
+
+ // Test single job.
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Test overlapping jobs.
+ JobStatus jobStatus2 = createJobStatus("testTimerTracking_AllBackground", 2);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+
+ JobStatus jobStatus3 = createJobStatus("testTimerTracking_AllBackground", 3);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /** Tests that Timers don't count foreground jobs. */
+ @Test
+ public void testTimerTracking_AllForeground() {
+ setDischarging();
+
+ JobStatus jobStatus = createJobStatus("testTimerTracking_AllForeground", 1);
+ jobStatus.uidActive = true;
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that Timers properly track overlapping foreground and background jobs.
+ */
+ @Test
+ public void testTimerTracking_ForegroundAndBackground() {
+ setDischarging();
+
+ JobStatus jobBg1 = createJobStatus("testTimerTracking_ForegroundAndBackground", 1);
+ JobStatus jobBg2 = createJobStatus("testTimerTracking_ForegroundAndBackground", 2);
+ JobStatus jobFg3 = createJobStatus("testTimerTracking_ForegroundAndBackground", 3);
+ jobFg3.uidActive = true;
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expected = new ArrayList<>();
+
+ // UID starts out inactive.
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Bg job starts while inactive, spans an entire active session, and ends after the
+ // active session.
+ // Fg job starts after the bg job and ends before the bg job.
+ // Entire bg job duration should be counted since it started before active session. However,
+ // count should only be 1 since Timer shouldn't count fg jobs.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobFg3);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ expected.add(createTimingSession(start, 30 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes
+ // "inactive" and then bg job 2 starts. Then fg job ends.
+ // This should result in two TimingSessions with a count of one each.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobFg3);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
+ advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches
+ * its quota.
+ */
+ @Test
+ public void testTracking_OutOfQuota() {
+ JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+ // Now the package only has two seconds to run.
+ final long remainingTimeMs = 2 * SECOND_IN_MILLIS;
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+ 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+
+ // Start the job.
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(remainingTimeMs);
+
+ // Wait for some extra time to allow for job processing.
+ verify(mJobSchedulerService,
+ timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged();
+ assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ }
+
+ /**
+ * Tests that a job is properly handled when it's at the edge of its quota and the old quota is
+ * being phased out.
+ */
+ @Test
+ public void testTracking_RollingQuota() {
+ JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+ Handler handler = mQuotaController.getHandler();
+ spyOn(handler);
+
+ long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long remainingTimeMs = SECOND_IN_MILLIS;
+ // The package only has one second to run, but this session is at the edge of the rolling
+ // window, so as the package "reaches its quota" it will have more to keep running.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 2 * HOUR_IN_MILLIS,
+ 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+
+ assertEquals(remainingTimeMs, mQuotaController.getRemainingExecutionTimeLocked(jobStatus));
+ // Start the job.
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ advanceElapsedClock(remainingTimeMs);
+
+ // Wait for some extra time to allow for job processing.
+ verify(mJobSchedulerService,
+ timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+ .onControllerStateChanged();
+ assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ // The job used up the remaining quota, but in that time, the same amount of time in the
+ // old TimingSession also fell out of the quota window, so it should still have the same
+ // amount of remaining time left its quota.
+ assertEquals(remainingTimeMs,
+ mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ verify(handler, atLeast(1)).sendMessageDelayed(any(), eq(remainingTimeMs));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java
new file mode 100644
index 0000000..db69242
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.app.AlarmManager;
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerService.Constants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+import java.util.function.Predicate;
+
+@RunWith(AndroidJUnit4.class)
+public class StateControllerTest {
+ private static final long SECOND_IN_MILLIS = 1000L;
+ private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+ private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+ private static final int CALLING_UID = 1000;
+ private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+ private static final int SOURCE_USER_ID = 0;
+
+ private Constants mConstants;
+ private StateController mStateController;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private JobSchedulerService mJobSchedulerService;
+
+ private class TestStateController extends StateController {
+ TestStateController(JobSchedulerService service) {
+ super(service);
+ }
+
+ public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+ }
+
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+ }
+
+ public void dumpControllerStateLocked(IndentingPrintWriter pw,
+ Predicate<JobStatus> predicate) {
+ }
+
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+ Predicate<JobStatus> predicate) {
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ // Use default constants for now.
+ mConstants = new Constants();
+
+ // Called in StateController constructor.
+ when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+ when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+ when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+ // Called in QuotaController constructor.
+ // Used in JobStatus.
+ doReturn(mock(PackageManagerInternal.class))
+ .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+ // Freeze the clocks at this moment in time
+ JobSchedulerService.sSystemClock =
+ Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sUptimeMillisClock =
+ Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+ // Initialize real objects.
+ mStateController = new TestStateController(mJobSchedulerService);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private JobStatus createJobStatus(String testTag, int jobId) {
+ JobInfo jobInfo = new JobInfo.Builder(jobId,
+ new ComponentName(mContext, "TestQuotaJobService"))
+ .setMinimumLatency(Math.abs(jobId) + 1)
+ .build();
+ return JobStatus.createFromJobInfo(
+ jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ }
+
+ @Test
+ public void testWouldBeReadyWithConstraintLocked() {
+ JobStatus job = spy(createJobStatus("testWouldBeReadyWithConstraintLocked", 1));
+
+ when(job.wouldBeReadyWithConstraint(anyInt())).thenReturn(false);
+ assertFalse(mStateController.wouldBeReadyWithConstraintLocked(job, 1));
+
+ when(job.wouldBeReadyWithConstraint(anyInt())).thenReturn(true);
+ when(mJobSchedulerService.areComponentsInPlaceLocked(job)).thenReturn(false);
+ assertFalse(mStateController.wouldBeReadyWithConstraintLocked(job, 1));
+
+ when(job.wouldBeReadyWithConstraint(anyInt())).thenReturn(true);
+ when(mJobSchedulerService.areComponentsInPlaceLocked(job)).thenReturn(true);
+ assertTrue(mStateController.wouldBeReadyWithConstraintLocked(job, 1));
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index e804342..d7b1cb4 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -69,10 +69,10 @@
"liblog",
"liblzma",
"libnativehelper",
- "libnetdaidl",
"libui",
"libunwind",
"libutils",
+ "netd_aidl_interface-cpp",
],
dxflags: ["--multi-dex"],
@@ -88,7 +88,7 @@
"utils/**/*.java",
],
static_libs: [
- "android-support-test",
+ "junit",
"mockito-target-minus-junit4",
],
libs: [
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 746c453..cf4d3a8 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -66,6 +66,7 @@
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
new file mode 100644
index 0000000..f75617e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BinderCallsStatsService.AuthorizedWorkSourceProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class BinderCallsStatsServiceTest {
+ @Test
+ public void weTrustOurselves() {
+ AuthorizedWorkSourceProvider workSourceProvider = new AuthorizedWorkSourceProvider() {
+ protected int getCallingUid() {
+ return Process.myUid();
+ }
+
+ protected int getCallingWorkSourceUid() {
+ return 1;
+ }
+ };
+ workSourceProvider.systemReady(InstrumentationRegistry.getContext());
+
+ assertEquals(1, workSourceProvider.resolveWorkSourceUid());
+ }
+
+ @Test
+ public void workSourceSetIfCallerHasPermission() {
+ AuthorizedWorkSourceProvider workSourceProvider = new AuthorizedWorkSourceProvider() {
+ protected int getCallingUid() {
+ // System process uid which as UPDATE_DEVICE_STATS.
+ return 1001;
+ }
+
+ protected int getCallingWorkSourceUid() {
+ return 1;
+ }
+ };
+ workSourceProvider.systemReady(InstrumentationRegistry.getContext());
+
+ assertEquals(1, workSourceProvider.resolveWorkSourceUid());
+ }
+
+ @Test
+ public void workSourceResolvedToCallingUid() {
+ AuthorizedWorkSourceProvider workSourceProvider = new AuthorizedWorkSourceProvider() {
+ protected int getCallingUid() {
+ // UID without permissions.
+ return Integer.MAX_VALUE;
+ }
+
+ protected int getCallingWorkSourceUid() {
+ return 1;
+ }
+ };
+ workSourceProvider.systemReady(InstrumentationRegistry.getContext());
+
+ assertEquals(Integer.MAX_VALUE, workSourceProvider.resolveWorkSourceUid());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 81107cf..2a78b6f 100644
--- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -17,9 +17,10 @@
package com.android.server;
-import static org.mockito.Mockito.when;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
@@ -134,4 +135,44 @@
mContext.sendBroadcast(intentUnplugged);
assertThat(deviceState.isCharging()).isFalse();
}
+
+ @Test
+ public void correctlyTracksTimeOnBattery() throws Exception {
+ CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+ when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+
+ service.onStart();
+ CachedDeviceState.Readonly deviceState =
+ LocalServices.getService(CachedDeviceState.Readonly.class);
+
+ CachedDeviceState.TimeInStateStopwatch stopwatch =
+ deviceState.createTimeOnBatteryStopwatch();
+
+ // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+ assertThat(stopwatch.isRunning()).isFalse();
+ service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ assertThat(stopwatch.isRunning()).isTrue();
+ stopwatch.reset();
+
+ Thread.sleep(100);
+ assertThat(stopwatch.isRunning()).isTrue();
+ assertThat(stopwatch.getMillis()).isAtLeast(100L);
+
+ long timeOnBatteryBeforePluggedIn = stopwatch.getMillis();
+ Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+ mContext.sendBroadcast(intentPluggedIn);
+
+ assertThat(stopwatch.getMillis()).isAtLeast(timeOnBatteryBeforePluggedIn);
+ assertThat(stopwatch.isRunning()).isFalse();
+
+ long timeOnBatteryAfterPluggedIn = stopwatch.getMillis();
+ Thread.sleep(20);
+ assertThat(stopwatch.getMillis()).isEqualTo(timeOnBatteryAfterPluggedIn);
+
+ stopwatch.reset();
+ assertThat(stopwatch.getMillis()).isEqualTo(0L);
+ assertThat(stopwatch.isRunning()).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 7c00299..751ed9b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -23,11 +23,13 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -43,10 +45,14 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.test.FakeSettingsProvider;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -81,8 +87,9 @@
new ComponentName("package1", "class1"),
new ComponentName("package2", "class2")
};
- private final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+ private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+ @UserIdInt private int mUserId;
@Mock private BackupManagerService mBackupManagerServiceMock;
@Mock private Context mContextMock;
@Mock private File mSuppressFileMock;
@@ -97,6 +104,7 @@
private FileDescriptor mFileDescriptorStub = new FileDescriptor();
private TrampolineTestable mTrampoline;
+ private MockContentResolver mContentResolver;
@Before
public void setUp() {
@@ -104,12 +112,18 @@
TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
TrampolineTestable.sSuppressFile = mSuppressFileMock;
+ TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
TrampolineTestable.sBackupDisabled = false;
when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
+ mUserId = NON_USER_SYSTEM;
mTrampoline = new TrampolineTestable(mContextMock);
+
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContextMock.getContentResolver()).thenReturn(mContentResolver);
}
@Test
@@ -118,6 +132,24 @@
}
@Test
+ public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+
+ mTrampoline.startServiceForUser(10);
+
+ verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
+ }
+
+ @Test
+ public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+
+ mTrampoline.startServiceForUser(10);
+
+ verify(mBackupManagerServiceMock).startServiceForUser(10);
+ }
+
+ @Test
public void initializeService_forUserSystem_successfullyInitialized() {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
@@ -333,10 +365,22 @@
}
@Test
- public void setBackupEnabled_forwarded() throws RemoteException {
+ public void setBackupEnabledForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.setBackupEnabledForUser(mUserId, true);
+
+ verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
+ }
+
+ @Test
+ public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.setBackupEnabled(true);
- verify(mBackupManagerServiceMock).setBackupEnabled(true);
+
+ verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
}
@Test
@@ -372,10 +416,22 @@
}
@Test
- public void isBackupEnabled_forwarded() throws RemoteException {
+ public void isBackupEnabledForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.isBackupEnabledForUser(mUserId);
+
+ verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
+ }
+
+ @Test
+ public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.isBackupEnabled();
- verify(mBackupManagerServiceMock).isBackupEnabled();
+
+ verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
}
@Test
@@ -411,10 +467,22 @@
}
@Test
- public void backupNow_forwarded() throws RemoteException {
+ public void backupNowForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.backupNowForUser(mUserId);
+
+ verify(mBackupManagerServiceMock).backupNow(mUserId);
+ }
+
+ @Test
+ public void backupNow_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.backupNow();
- verify(mBackupManagerServiceMock).backupNow();
+
+ verify(mBackupManagerServiceMock).backupNow(mUserId);
}
@Test
@@ -770,15 +838,28 @@
}
@Test
- public void requestBackup_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.requestBackup(PACKAGE_NAMES, mBackupObserverMock,
- mBackupManagerMonitorMock, 123)).thenReturn(456);
-
+ public void requestBackupForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES, mBackupObserverMock,
- mBackupManagerMonitorMock, 123));
- verify(mBackupManagerServiceMock).requestBackup(PACKAGE_NAMES, mBackupObserverMock,
- mBackupManagerMonitorMock, 123);
+
+ assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123));
+ verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123);
+ }
+
+ @Test
+ public void requestBackup_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123));
+ verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123);
}
@Test
@@ -788,10 +869,22 @@
}
@Test
- public void cancelBackups_forwarded() throws RemoteException {
+ public void cancelBackupsForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.cancelBackupsForUser(mUserId);
+
+ verify(mBackupManagerServiceMock).cancelBackups(mUserId);
+ }
+
+ @Test
+ public void cancelBackups_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.cancelBackups();
- verify(mBackupManagerServiceMock).cancelBackups();
+
+ verify(mBackupManagerServiceMock).cancelBackups(mUserId);
}
@Test
@@ -863,6 +956,7 @@
private static class TrampolineTestable extends Trampoline {
static boolean sBackupDisabled = false;
static File sSuppressFile = null;
+ static int sCallingUserId = -1;
static int sCallingUid = -1;
static BackupManagerService sBackupManagerServiceMock = null;
private int mCreateServiceCallsCount = 0;
@@ -881,6 +975,10 @@
return sSuppressFile;
}
+ protected int binderGetCallingUserId() {
+ return sCallingUserId;
+ }
+
@Override
protected int binderGetCallingUid() {
return sCallingUid;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 5dc6d83..729fac5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -21,6 +21,9 @@
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
@@ -48,6 +51,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+import static org.testng.Assert.assertThrows;
import android.Manifest.permission;
import android.annotation.RawRes;
@@ -4163,7 +4167,7 @@
// test reset password with token
when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password),
eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD),
- eq(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED), eq(handle), eq(token),
+ eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token),
eq(UserHandle.USER_SYSTEM)))
.thenReturn(true);
assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0));
@@ -5133,6 +5137,71 @@
});
}
+ public void testGetPasswordComplexity_securityExceptionIfParentInstance() {
+ assertThrows(SecurityException.class,
+ () -> new DevicePolicyManagerTestable(
+ mServiceContext,
+ dpms,
+ /* parentInstance= */ true)
+ .getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_illegalStateExceptionIfLocked() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(false);
+ assertThrows(IllegalStateException.class, () -> dpm.getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_securityExceptionWithoutPermissions() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ assertThrows(SecurityException.class, () -> dpm.getPasswordComplexity());
+ }
+
+
+ public void testGetPasswordComplexity_currentUserNoPassword() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(DpmMockContext.CALLER_USER_HANDLE);
+
+ assertEquals(PASSWORD_COMPLEXITY_NONE, dpm.getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_currentUserHasPassword() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(DpmMockContext.CALLER_USER_HANDLE);
+ dpms.mUserPasswordMetrics.put(
+ DpmMockContext.CALLER_USER_HANDLE,
+ PasswordMetrics.computeForPassword("asdf"));
+
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM, dpm.getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_unifiedChallengeReturnsParentUserPassword() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+
+ UserInfo parentUser = new UserInfo();
+ parentUser.id = DpmMockContext.CALLER_USER_HANDLE + 10;
+ when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(parentUser.id);
+
+ dpms.mUserPasswordMetrics.put(
+ DpmMockContext.CALLER_USER_HANDLE,
+ PasswordMetrics.computeForPassword("asdf"));
+ dpms.mUserPasswordMetrics.put(
+ parentUser.id,
+ PasswordMetrics.computeForPassword("parentUser"));
+
+ assertEquals(PASSWORD_COMPLEXITY_HIGH, dpm.getPasswordComplexity());
+ }
+
private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
final long ident = mServiceContext.binder.clearCallingIdentity();
mServiceContext.binder.callingUid =
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
index 3da61d6..4982d6e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
@@ -26,7 +26,12 @@
public DevicePolicyManagerTestable(DpmMockContext context,
DevicePolicyManagerServiceTestable dpms) {
- super(context, dpms, /* parentInstance = */ false);
+ this(context, dpms, /* parentInstance= */ false);
+ }
+
+ public DevicePolicyManagerTestable(DpmMockContext context,
+ DevicePolicyManagerServiceTestable dpms, boolean parentInstance) {
+ super(context, dpms, parentInstance);
this.dpms = dpms;
}
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index 53711a6..e0ecd3e 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -35,6 +35,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.internal.app.ColorDisplayController;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
@@ -911,7 +912,11 @@
startService();
assertAccessibilityTransformActivated(true /* activated */ );
assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+ if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+ assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+ } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+ assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+ }
}
@Test
@@ -926,7 +931,11 @@
startService();
assertAccessibilityTransformActivated(true /* activated */ );
assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+ if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+ assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+ } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+ assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+ }
}
@Test
@@ -942,7 +951,11 @@
startService();
assertAccessibilityTransformActivated(true /* activated */ );
assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+ if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+ assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+ } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+ assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+ }
}
@Test
@@ -1030,6 +1043,24 @@
}
/**
+ * Returns whether the color mode is valid on the device the tests are running on.
+ *
+ * @param mode the mode to check
+ */
+ private boolean isColorModeValid(int mode) {
+ final int[] availableColorModes = mContext.getResources().getIntArray(
+ R.array.config_availableColorModes);
+ if (availableColorModes != null) {
+ for (int availableMode : availableColorModes) {
+ if (mode == availableMode) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Convenience method to start {@link #mColorDisplayService}.
*/
private void startService() {
@@ -1038,7 +1069,6 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- mColorDisplayService.onStart();
mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mColorDisplayService.onStartUser(mUserId);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index b421280..e9bfa8f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -18,11 +18,21 @@
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
@@ -31,9 +41,9 @@
import android.view.DisplayInfo;
import android.view.SurfaceControl;
-import androidx.test.runner.AndroidJUnit4;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -49,17 +59,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Arrays;
import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
+import java.util.stream.LongStream;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -397,6 +398,43 @@
displayManager.validateBrightnessConfiguration(null);
}
+ /**
+ * Tests that collection of display color sampling results are sensible.
+ */
+ @Test
+ public void testDisplayedContentSampling() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ registerDefaultDisplays(displayManager);
+
+ DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(0);
+ assertNotNull(ddi);
+
+ DisplayedContentSamplingAttributes attr =
+ displayManager.getDisplayedContentSamplingAttributesInternal(0);
+ if (attr == null) return; //sampling not supported on device, skip remainder of test.
+
+ boolean enabled = displayManager.setDisplayedContentSamplingEnabledInternal(0, true, 0, 0);
+ assertTrue(!enabled);
+
+ displayManager.setDisplayedContentSamplingEnabledInternal(0, false, 0, 0);
+ DisplayedContentSample sample = displayManager.getDisplayedContentSampleInternal(0, 0, 0);
+ assertNotNull(sample);
+
+ long numPixels = ddi.width * ddi.height * sample.getNumFrames();
+ long[] samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL0);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+ samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL1);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+ samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL2);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+ samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL3);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+ }
+
private void registerDefaultDisplays(DisplayManagerService displayManager) {
Handler handler = displayManager.getDisplayHandler();
// Would prefer to call displayManager.onStart() directly here but it performs binderService
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
deleted file mode 100644
index 5b59e60..0000000
--- a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.job.controllers;
-
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.when;
-
-import android.app.job.JobInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManagerInternal;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkPolicyManager;
-import android.os.Build;
-import android.os.SystemClock;
-import android.util.DataUnit;
-
-import com.android.server.LocalServices;
-import com.android.server.job.JobSchedulerService;
-import com.android.server.job.JobSchedulerService.Constants;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.time.Clock;
-import java.time.ZoneOffset;
-
-@RunWith(MockitoJUnitRunner.class)
-public class ConnectivityControllerTest {
-
- @Mock private Context mContext;
- @Mock private ConnectivityManager mConnManager;
- @Mock private NetworkPolicyManager mNetPolicyManager;
- @Mock private JobSchedulerService mService;
-
- private Constants mConstants;
-
- private static final int UID_RED = 10001;
- private static final int UID_BLUE = 10002;
-
- @Before
- public void setUp() throws Exception {
- // Assume all packages are current SDK
- final PackageManagerInternal pm = mock(PackageManagerInternal.class);
- when(pm.getPackageTargetSdkVersion(anyString()))
- .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, pm);
-
- // Freeze the clocks at this moment in time
- JobSchedulerService.sSystemClock =
- Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
- JobSchedulerService.sUptimeMillisClock =
- Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
- JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
-
- // Assume default constants for now
- mConstants = new Constants();
-
- // Get our mocks ready
- when(mContext.getSystemServiceName(ConnectivityManager.class))
- .thenReturn(Context.CONNECTIVITY_SERVICE);
- when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
- .thenReturn(mConnManager);
- when(mContext.getSystemServiceName(NetworkPolicyManager.class))
- .thenReturn(Context.NETWORK_POLICY_SERVICE);
- when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
- .thenReturn(mNetPolicyManager);
- when(mService.getTestableContext()).thenReturn(mContext);
- when(mService.getLock()).thenReturn(mService);
- when(mService.getConstants()).thenReturn(mConstants);
- }
-
- @Test
- public void testInsane() throws Exception {
- final Network net = new Network(101);
- final JobInfo.Builder job = createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
-
- // Slow network is too slow
- assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1)
- .setLinkDownstreamBandwidthKbps(1), mConstants));
- // Fast network looks great
- assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1024)
- .setLinkDownstreamBandwidthKbps(1024), mConstants));
- }
-
- @Test
- public void testCongestion() throws Exception {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- final JobInfo.Builder job = createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
- final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
- final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
-
- // Uncongested network is whenever
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED);
- assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- }
-
- // Congested network is more selective
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities();
- assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- }
- }
-
- @Test
- public void testRelaxed() throws Exception {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- final JobInfo.Builder job = createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
- final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
- final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
-
- job.setIsPrefetch(true);
- final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
- final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
-
- // Unmetered network is whenever
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED)
- .addCapability(NET_CAPABILITY_NOT_METERED);
- assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
- }
-
- // Metered network is only when prefetching and late
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED);
- assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertFalse(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
- }
- }
-
- @Test
- public void testUpdates() throws Exception {
- final ArgumentCaptor<NetworkCallback> callback = ArgumentCaptor
- .forClass(NetworkCallback.class);
- doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
-
- final ConnectivityController controller = new ConnectivityController(mService);
-
- final Network meteredNet = new Network(101);
- final NetworkCapabilities meteredCaps = createCapabilities();
- final Network unmeteredNet = new Network(202);
- final NetworkCapabilities unmeteredCaps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_METERED);
-
- final JobStatus red = createJobStatus(createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
- final JobStatus blue = createJobStatus(createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
-
- // Pretend we're offline when job is added
- {
- reset(mConnManager);
- answerNetwork(UID_RED, null, null);
- answerNetwork(UID_BLUE, null, null);
-
- controller.maybeStartTrackingJobLocked(red, null);
- controller.maybeStartTrackingJobLocked(blue, null);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Metered network
- {
- reset(mConnManager);
- answerNetwork(UID_RED, meteredNet, meteredCaps);
- answerNetwork(UID_BLUE, meteredNet, meteredCaps);
-
- callback.getValue().onCapabilitiesChanged(meteredNet, meteredCaps);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Unmetered network background
- {
- reset(mConnManager);
- answerNetwork(UID_RED, meteredNet, meteredCaps);
- answerNetwork(UID_BLUE, meteredNet, meteredCaps);
-
- callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Lost metered network
- {
- reset(mConnManager);
- answerNetwork(UID_RED, unmeteredNet, unmeteredCaps);
- answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
-
- callback.getValue().onLost(meteredNet);
-
- assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Specific UID was blocked
- {
- reset(mConnManager);
- answerNetwork(UID_RED, null, null);
- answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
-
- callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
- }
-
- private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
- when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
- when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
- if (net != null) {
- final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
- ni.setDetailedState(DetailedState.CONNECTED, null, null);
- when(mConnManager.getNetworkInfoForUid(eq(net), eq(uid), anyBoolean())).thenReturn(ni);
- }
- }
-
- private static NetworkCapabilities createCapabilities() {
- return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NET_CAPABILITY_VALIDATED);
- }
-
- private static JobInfo.Builder createJob() {
- return new JobInfo.Builder(101, new ComponentName("foo", "bar"));
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job) {
- return createJobStatus(job, android.os.Process.NOBODY_UID, 0, Long.MAX_VALUE);
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job, int uid) {
- return createJobStatus(job, uid, 0, Long.MAX_VALUE);
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job,
- long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
- return createJobStatus(job, android.os.Process.NOBODY_UID,
- earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
- long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
- return new JobStatus(job.build(), uid, null, -1, 0, 0, null,
- earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index c4c2ad9..839b25f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -212,7 +212,29 @@
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PRIMARY_USER)));
+ UserHandle.of(PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_currentUser() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -234,7 +256,31 @@
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_notInstalled() {
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -254,7 +300,29 @@
mIApplicationThread,
PACKAGE_TWO,
ACTIVITY_COMPONENT,
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_fakeCaller() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_TWO,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -276,7 +344,31 @@
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_notExported() {
+ mActivityInfo.exported = false;
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -296,7 +388,29 @@
mIApplicationThread,
PACKAGE_ONE,
new ComponentName(PACKAGE_TWO, "test"),
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_anotherPackage() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ new ComponentName(PACKAGE_TWO, "test"),
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -316,7 +430,29 @@
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(SECONDARY_USER)));
+ UserHandle.of(SECONDARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_secondaryUser() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(SECONDARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -335,7 +471,8 @@
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PRIMARY_USER));
+ UserHandle.of(PRIMARY_USER).getIdentifier(),
+ true);
verify(mActivityTaskManagerInternal)
.startActivityAsUser(
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7bcb571..823b7a5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,13 +16,13 @@
package com.android.server.pm;
+import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.app.ActivityManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -162,6 +162,29 @@
}
@MediumTest
+ public void testRemoveUserByHandle() {
+ UserInfo userInfo = createUser("Guest 1", UserInfo.FLAG_GUEST);
+ final UserHandle user = userInfo.getUserHandle();
+ synchronized (mUserRemoveLock) {
+ mUserManager.removeUser(user);
+ long time = System.currentTimeMillis();
+ while (mUserManager.getUserInfo(user.getIdentifier()) != null) {
+ try {
+ mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) {
+ fail("Timeout waiting for removeUser. userId = " + user.getIdentifier());
+ }
+ }
+ }
+
+ assertFalse(findUser(userInfo.id));
+ }
+
+ @MediumTest
public void testAddGuest() throws Exception {
UserInfo userInfo1 = createUser("Guest 1", UserInfo.FLAG_GUEST);
UserInfo userInfo2 = createUser("Guest 2", UserInfo.FLAG_GUEST);
@@ -504,9 +527,21 @@
UserInfo user = createUser("User", 0);
assertNotNull(user);
// Switch to the user just created.
- switchUser(user.id);
+ switchUser(user.id, null, true);
// Switch back to the starting user.
- switchUser(startUser);
+ switchUser(startUser, null, true);
+ }
+
+ @LargeTest
+ public void testSwitchUserByHandle() {
+ ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ final int startUser = am.getCurrentUser();
+ UserInfo user = createUser("User", 0);
+ assertNotNull(user);
+ // Switch to the user just created.
+ switchUser(-1, user.getUserHandle(), false);
+ // Switch back to the starting user.
+ switchUser(-1, UserHandle.of(startUser), false);
}
@MediumTest
@@ -544,10 +579,20 @@
}
}
- private void switchUser(int userId) {
+ /**
+ * @param userId value will be used to call switchUser(int) only if ignoreHandle is false.
+ * @param user value will be used to call switchUser(UserHandle) only if ignoreHandle is true.
+ * @param ignoreHandle if true, switchUser(int) will be called with the provided userId,
+ * else, switchUser(UserHandle) will be called with the provided user.
+ */
+ private void switchUser(int userId, UserHandle user, boolean ignoreHandle) {
synchronized (mUserSwitchLock) {
ActivityManager am = getContext().getSystemService(ActivityManager.class);
- am.switchUser(userId);
+ if (ignoreHandle) {
+ am.switchUser(userId);
+ } else {
+ am.switchUser(user);
+ }
long time = System.currentTimeMillis();
try {
mUserSwitchLock.wait(SWITCH_USER_TIMEOUT_MILLIS);
@@ -556,7 +601,8 @@
return;
}
if (System.currentTimeMillis() - time > SWITCH_USER_TIMEOUT_MILLIS) {
- fail("Timeout waiting for the user switch to u" + userId);
+ fail("Timeout waiting for the user switch to u"
+ + (ignoreHandle ? userId : user.getIdentifier()));
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index dad7b93..fd07cb0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -18,10 +18,14 @@
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -41,6 +45,10 @@
import com.android.server.pm.Installer;
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+import dalvik.system.VMRuntime;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -50,10 +58,6 @@
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
-import dalvik.system.DelegateLastClassLoader;
-import dalvik.system.PathClassLoader;
-import dalvik.system.VMRuntime;
-
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -129,6 +133,9 @@
// Package is not used by others, so we should get nothing back.
assertNoUseInfo(mFooUser0);
+
+ // A package loading its own code is not stored as DCL.
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -140,6 +147,8 @@
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertIsUsedByOtherApps(mBarUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+ assertHasDclInfo(mBarUser0, mFooUser0, mBarUser0.getBaseAndSplitDexPaths());
}
@Test
@@ -152,6 +161,8 @@
assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
}
@Test
@@ -164,6 +175,8 @@
assertIsUsedByOtherApps(mBarUser0, pui, false);
assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
+
+ assertHasDclInfo(mBarUser0, mFooUser0, barSecondaries);
}
@Test
@@ -200,9 +213,10 @@
}
@Test
- public void testPackageUseInfoNotFound() {
+ public void testNoNotify() {
// Assert we don't get back data we did not previously record.
assertNoUseInfo(mFooUser0);
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -210,6 +224,7 @@
// Notifying with an invalid ISA should be ignored.
notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
assertNoUseInfo(mInvalidIsa);
+ assertNoDclInfo(mInvalidIsa);
}
@Test
@@ -218,6 +233,7 @@
// register in DexManager#load should be ignored.
notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
assertNoUseInfo(mDoesNotExist);
+ assertNoDclInfo(mDoesNotExist);
}
@Test
@@ -226,6 +242,8 @@
// Request should be ignored.
notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
assertNoUseInfo(mBarUser1);
+
+ assertNoDclInfo(mBarUser1);
}
@Test
@@ -235,6 +253,10 @@
// still check that nothing goes unexpected in DexManager.
notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
assertNoUseInfo(mBarUser1);
+ assertNoUseInfo(mFooUser0);
+
+ assertNoDclInfo(mBarUser1);
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -247,6 +269,7 @@
// is trying to load something from it we should not find it.
notifyDexLoad(mFooUser0, newSecondaries, mUser0);
assertNoUseInfo(newPackage);
+ assertNoDclInfo(newPackage);
// Notify about newPackage install and let mFoo load its dexes.
mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
@@ -257,6 +280,7 @@
assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
+ assertHasDclInfo(newPackage, mFooUser0, newSecondaries);
}
@Test
@@ -273,6 +297,7 @@
assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
+ assertHasDclInfo(newPackage, newPackage, newSecondaries);
}
@Test
@@ -305,6 +330,7 @@
// We shouldn't find yet the new split as we didn't notify the package update.
notifyDexLoad(mFooUser0, newSplits, mUser0);
assertNoUseInfo(mBarUser0);
+ assertNoDclInfo(mBarUser0);
// Notify that bar is updated. splitSourceDirs will contain the updated path.
mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
@@ -314,8 +340,8 @@
// Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
notifyDexLoad(mFooUser0, newSplits, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
assertIsUsedByOtherApps(newSplits, pui, true);
+ assertHasDclInfo(mBarUser0, mFooUser0, newSplits);
}
@Test
@@ -326,11 +352,15 @@
mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
- // Bar should not be around since it was removed for all users.
+ // Data for user 1 should still be present
PackageUseInfo pui = getPackageUseInfo(mBarUser1);
- assertNotNull(pui);
assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
/*isUsedByOtherApps*/false, mUser1);
+ assertHasDclInfo(mBarUser1, mBarUser1, mBarUser1.getSecondaryDexPaths());
+
+ // But not user 0
+ assertNoUseInfo(mBarUser0, mUser0);
+ assertNoDclInfo(mBarUser0, mUser0);
}
@Test
@@ -349,6 +379,8 @@
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
assertIsUsedByOtherApps(mFooUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -362,6 +394,7 @@
// Foo should not be around since all its secondary dex info were deleted
// and it is not used by other apps.
assertNoUseInfo(mFooUser0);
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -374,6 +407,7 @@
// Bar should not be around since it was removed for all users.
assertNoUseInfo(mBarUser0);
+ assertNoDclInfo(mBarUser0);
}
@Test
@@ -383,6 +417,7 @@
notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
// The dex file should not be recognized as a package.
assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
+ assertNull(mDexManager.getPackageDynamicCodeInfo(frameworkDex));
}
@Test
@@ -395,6 +430,8 @@
assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
}
@Test
@@ -402,7 +439,12 @@
List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
+ // We don't record the dex usage
assertNoUseInfo(mBarUser0UnsupportedClassLoader);
+
+ // But we do record this as an intance of dynamic code loading
+ assertHasDclInfo(
+ mBarUser0UnsupportedClassLoader, mBarUser0UnsupportedClassLoader, secondaries);
}
@Test
@@ -414,6 +456,8 @@
notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0);
assertNoUseInfo(mBarUser0);
+
+ assertHasDclInfo(mBarUser0, mBarUser0, mBarUser0.getSecondaryDexPaths());
}
@Test
@@ -421,6 +465,7 @@
notifyDexLoad(mBarUser0, null, mUser0);
assertNoUseInfo(mBarUser0);
+ assertNoDclInfo(mBarUser0);
}
@Test
@@ -455,12 +500,14 @@
notifyDexLoad(mBarUser0, secondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+ assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
// Record bar secondaries again with an unsupported class loader. This should not change the
// context.
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
pui = getPackageUseInfo(mBarUser0);
assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+ assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
}
@Test
@@ -533,13 +580,53 @@
private PackageUseInfo getPackageUseInfo(TestData testData) {
assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
- return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
+ PackageUseInfo pui = mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
+ assertNotNull(pui);
+ return pui;
}
private void assertNoUseInfo(TestData testData) {
assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
}
+ private void assertNoUseInfo(TestData testData, int userId) {
+ if (!mDexManager.hasInfoOnPackage(testData.getPackageName())) {
+ return;
+ }
+ PackageUseInfo pui = getPackageUseInfo(testData);
+ for (DexUseInfo dexUseInfo : pui.getDexUseInfoMap().values()) {
+ assertNotEquals(userId, dexUseInfo.getOwnerUserId());
+ }
+ }
+
+ private void assertNoDclInfo(TestData testData) {
+ assertNull(mDexManager.getPackageDynamicCodeInfo(testData.getPackageName()));
+ }
+
+ private void assertNoDclInfo(TestData testData, int userId) {
+ PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(testData.getPackageName());
+ if (info == null) {
+ return;
+ }
+
+ for (DynamicCodeFile fileInfo : info.mFileUsageMap.values()) {
+ assertNotEquals(userId, fileInfo.mUserId);
+ }
+ }
+
+ private void assertHasDclInfo(TestData owner, TestData loader, List<String> paths) {
+ PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(owner.getPackageName());
+ assertNotNull("No DCL data for owner " + owner.getPackageName(), info);
+ for (String path : paths) {
+ DynamicCodeFile fileInfo = info.mFileUsageMap.get(path);
+ assertNotNull("No DCL data for path " + path, fileInfo);
+ assertEquals(PackageDynamicCodeLoading.FILE_TYPE_DEX, fileInfo.mFileType);
+ assertEquals(owner.mUserId, fileInfo.mUserId);
+ assertTrue("No DCL data for loader " + loader.getPackageName(),
+ fileInfo.mLoadingPackages.contains(loader.getPackageName()));
+ }
+ }
+
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
PackageInfo pi = new PackageInfo();
pi.packageName = packageName;
@@ -563,11 +650,13 @@
private final PackageInfo mPackageInfo;
private final String mLoaderIsa;
private final String mClassLoader;
+ private final int mUserId;
private TestData(String packageName, String loaderIsa, int userId, String classLoader) {
mPackageInfo = getMockPackageInfo(packageName, userId);
mLoaderIsa = loaderIsa;
mClassLoader = classLoader;
+ mUserId = userId;
}
private TestData(String packageName, String loaderIsa, int userId) {
@@ -603,9 +692,7 @@
List<String> getBaseAndSplitDexPaths() {
List<String> paths = new ArrayList<>();
paths.add(mPackageInfo.applicationInfo.sourceDir);
- for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
- paths.add(split);
- }
+ Collections.addAll(paths, mPackageInfo.applicationInfo.splitSourceDirs);
return paths;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
new file mode 100644
index 0000000..eb4cc4e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
@@ -0,0 +1,560 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.escape;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.unescape;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PackageDynamicCodeLoadingTests {
+
+ // Deliberately making a copy here since we're testing identity and
+ // string literals have a tendency to be identical.
+ private static final String TRIVIAL_STRING = new String("hello/world");
+ private static final Entry[] NO_ENTRIES = {};
+ private static final String[] NO_PACKAGES = {};
+
+ @Test
+ public void testRecord() {
+ Entry[] entries = {
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+ new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+ new Entry("owning.package2", "/path/file3", 'D', 0, "loading.package2"),
+ };
+
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRecord_returnsHasChanged() {
+ Entry owner1Path1Loader1 =
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1");
+ Entry owner1Path1Loader2 =
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2");
+ Entry owner1Path2Loader1 =
+ new Entry("owning.package1", "/path/file2", 'D', 10, "loading.package1");
+ Entry owner2Path1Loader1 =
+ new Entry("owning.package2", "/path/file1", 'D', 10, "loading.package2");
+
+ PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+
+ assertTrue(record(info, owner1Path1Loader1));
+ assertFalse(record(info, owner1Path1Loader1));
+
+ assertTrue(record(info, owner1Path1Loader2));
+ assertFalse(record(info, owner1Path1Loader2));
+
+ assertTrue(record(info, owner1Path2Loader1));
+ assertFalse(record(info, owner1Path2Loader1));
+
+ assertTrue(record(info, owner2Path1Loader1));
+ assertFalse(record(info, owner2Path1Loader1));
+
+ assertHasEntries(info,
+ owner1Path1Loader1, owner1Path1Loader2, owner1Path2Loader1, owner2Path1Loader1);
+ }
+
+ @Test
+ public void testRecord_changeUserForFile_throws() {
+ Entry entry1 = new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1");
+ Entry entry2 = new Entry("owning.package1", "/path/file1", 'D', 20, "loading.package1");
+
+ PackageDynamicCodeLoading info = makePackageDcl(entry1);
+
+ assertThrows(() -> record(info, entry2));
+ assertHasEntries(info, entry1);
+ }
+
+ @Test
+ public void testRecord_badFileType_throws() {
+ Entry entry = new Entry("owning.package", "/path/file", 'Z', 10, "loading.package");
+ PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+
+ assertThrows(() -> record(info, entry));
+ }
+
+ @Test
+ public void testClear() {
+ Entry[] entries = {
+ new Entry("owner1", "file1", 'D', 10, "loader1"),
+ new Entry("owner2", "file2", 'D', 20, "loader2"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ info.clear();
+ assertHasEntries(info, NO_ENTRIES);
+ }
+
+ @Test
+ public void testRemovePackage_present() {
+ Entry other = new Entry("other", "file", 'D', 0, "loader");
+ Entry[] entries = {
+ new Entry("owner", "file1", 'D', 10, "loader1"),
+ new Entry("owner", "file2", 'D', 20, "loader2"),
+ other
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removePackage("owner"));
+ assertHasEntries(info, other);
+ assertHasPackages(info, "other");
+ }
+
+ @Test
+ public void testRemovePackage_notPresent() {
+ Entry[] entries = { new Entry("owner", "file", 'D', 0, "loader") };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removePackage("other"));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRemoveUserPackage_notPresent() {
+ Entry[] entries = { new Entry("owner", "file", 'D', 0, "loader") };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removeUserPackage("other", 0));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRemoveUserPackage_presentWithNoOtherUsers() {
+ Entry other = new Entry("other", "file", 'D', 0, "loader");
+ Entry[] entries = {
+ new Entry("owner", "file1", 'D', 0, "loader1"),
+ new Entry("owner", "file2", 'D', 0, "loader2"),
+ other
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeUserPackage("owner", 0));
+ assertHasEntries(info, other);
+ assertHasPackages(info, "other");
+ }
+
+ @Test
+ public void testRemoveUserPackage_presentWithUsers() {
+ Entry other = new Entry("owner", "file", 'D', 1, "loader");
+ Entry[] entries = {
+ new Entry("owner", "file1", 'D', 0, "loader1"),
+ new Entry("owner", "file2", 'D', 0, "loader2"),
+ other
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeUserPackage("owner", 0));
+ assertHasEntries(info, other);
+ }
+
+ @Test
+ public void testRemoveFile_present() {
+ Entry[] entries = {
+ new Entry("package1", "file1", 'D', 0, "loader1"),
+ new Entry("package1", "file2", 'D', 0, "loader1"),
+ new Entry("package2", "file1", 'D', 0, "loader2"),
+ };
+ Entry[] expectedSurvivors = {
+ new Entry("package1", "file2", 'D', 0, "loader1"),
+ new Entry("package2", "file1", 'D', 0, "loader2"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, expectedSurvivors);
+ }
+
+ @Test
+ public void testRemoveFile_onlyEntry() {
+ Entry[] entries = {
+ new Entry("package1", "file1", 'D', 0, "loader1"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, NO_ENTRIES);
+ assertHasPackages(info, NO_PACKAGES);
+ }
+
+ @Test
+ public void testRemoveFile_notPresent() {
+ Entry[] entries = {
+ new Entry("package1", "file2", 'D', 0, "loader1"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRemoveFile_wrongUser() {
+ Entry[] entries = {
+ new Entry("package1", "file1", 'D', 10, "loader1"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testSyncData() {
+ Map<String, Set<Integer>> packageToUsersMap = ImmutableMap.of(
+ "package1", ImmutableSet.of(10, 20),
+ "package2", ImmutableSet.of(20));
+
+ Entry[] entries = {
+ new Entry("deleted.packaged", "file1", 'D', 10, "package1"),
+ new Entry("package1", "file2", 'D', 20, "package2"),
+ new Entry("package1", "file3", 'D', 10, "package2"),
+ new Entry("package1", "file3", 'D', 10, "deleted.package"),
+ new Entry("package2", "file4", 'D', 20, "deleted.package"),
+ };
+
+ Entry[] expectedSurvivors = {
+ new Entry("package1", "file2", 'D', 20, "package2"),
+ };
+
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+ info.syncData(packageToUsersMap);
+ assertHasEntries(info, expectedSurvivors);
+ assertHasPackages(info, "package1");
+ }
+
+ @Test
+ public void testRead_onlyHeader_emptyResult() throws Exception {
+ assertHasEntries(read("DCL1"), NO_ENTRIES);
+ }
+
+ @Test
+ public void testRead_noHeader_throws() {
+ assertThrows(IOException.class, () -> read(""));
+ }
+
+ @Test
+ public void testRead_wrongHeader_throws() {
+ assertThrows(IOException.class, () -> read("DCL2"));
+ }
+
+ @Test
+ public void testRead_oneEntry() throws Exception {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:10:loading.package:/path/fi\\\\le\n";
+ assertHasEntries(read(inputText),
+ new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+ }
+
+ @Test
+ public void testRead_emptyPackage() throws Exception {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n";
+ PackageDynamicCodeLoading info = read(inputText);
+ assertHasEntries(info, NO_ENTRIES);
+ assertHasPackages(info, NO_PACKAGES);
+ }
+
+ @Test
+ public void testRead_complex() throws Exception {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package1\n"
+ + "D:10:loading.package1,loading.package2:/path/file1\n"
+ + "D:5:loading.package1:/path/file2\n"
+ + "P:owning.package2\n"
+ + "D:0:loading.package2:/path/file3";
+ assertHasEntries(read(inputText),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+ new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+ new Entry("owning.package2", "/path/file3", 'D', 0, "loading.package2"));
+ }
+
+ @Test
+ public void testRead_missingPackageLine_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "D:10:loading.package:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_malformedFile_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "Hello world!\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_badFileType_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "X:10:loading.package:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_badUserId_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:999999999999999999:loading.package:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_missingPackages_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:1:,:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testWrite_empty() throws Exception {
+ assertEquals("DCL1\n", write(NO_ENTRIES));
+ }
+
+ @Test
+ public void testWrite_oneEntry() throws Exception {
+ String expected = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:10:loading.package:/path/fi\\\\le\n";
+ String actual = write(
+ new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testWrite_complex_roundTrips() throws Exception {
+ // There isn't a canonical order for the output in the presence of multiple items.
+ // So we just check that if we read back what we write we end up where we started.
+ Entry[] entries = {
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+ new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+ new Entry("owning.package2", "/path/fi\\le3", 'D', 0, "loading.package2")
+ };
+ assertHasEntries(read(write(entries)), entries);
+ }
+
+ @Test
+ public void testWrite_failure_throws() {
+ PackageDynamicCodeLoading info = makePackageDcl(
+ new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+ assertThrows(IOException.class, () -> info.write(new ThrowingOutputStream()));
+ }
+
+ @Test
+ public void testEscape_trivialCase_returnsSameString() {
+ assertSame(TRIVIAL_STRING, escape(TRIVIAL_STRING));
+ }
+
+ @Test
+ public void testEscape() {
+ String input = "backslash\\newline\nreturn\r";
+ String expected = "backslash\\\\newline\\nreturn\\r";
+ assertEquals(expected, escape(input));
+ }
+
+ @Test
+ public void testUnescape_trivialCase_returnsSameString() throws Exception {
+ assertSame(TRIVIAL_STRING, unescape(TRIVIAL_STRING));
+ }
+
+ @Test
+ public void testUnescape() throws Exception {
+ String input = "backslash\\\\newline\\nreturn\\r";
+ String expected = "backslash\\newline\nreturn\r";
+ assertEquals(expected, unescape(input));
+ }
+
+ @Test
+ public void testUnescape_badEscape_throws() {
+ assertThrows(IOException.class, () -> unescape("this is \\bad"));
+ }
+
+ @Test
+ public void testUnescape_trailingBackslash_throws() {
+ assertThrows(IOException.class, () -> unescape("don't do this\\"));
+ }
+
+ @Test
+ public void testEscapeUnescape_roundTrips() throws Exception {
+ assertRoundTripsWithEscape("foo");
+ assertRoundTripsWithEscape("\\\\\n\n\r");
+ assertRoundTripsWithEscape("\\a\\b\\");
+ assertRoundTripsWithEscape("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\");
+ }
+
+ private void assertRoundTripsWithEscape(String original) throws Exception {
+ assertEquals(original, unescape(escape(original)));
+ }
+
+ private boolean record(PackageDynamicCodeLoading info, Entry entry) {
+ return info.record(entry.mOwningPackage, entry.mPath, entry.mFileType, entry.mUserId,
+ entry.mLoadingPackage);
+ }
+
+ private PackageDynamicCodeLoading read(String inputText) throws Exception {
+ ByteArrayInputStream inputStream =
+ new ByteArrayInputStream(inputText.getBytes(UTF_8));
+
+ PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+ info.read(inputStream);
+
+ return info;
+ }
+
+ private String write(Entry... entries) throws Exception {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ makePackageDcl(entries).write(output);
+ return new String(output.toByteArray(), UTF_8);
+ }
+
+ private Set<Entry> entriesFrom(PackageDynamicCodeLoading info) {
+ ImmutableSet.Builder<Entry> entries = ImmutableSet.builder();
+ for (String owningPackage : info.getAllPackagesWithDynamicCodeLoading()) {
+ PackageDynamicCode packageInfo = info.getPackageDynamicCodeInfo(owningPackage);
+ Map<String, DynamicCodeFile> usageMap = packageInfo.mFileUsageMap;
+ for (Map.Entry<String, DynamicCodeFile> fileEntry : usageMap.entrySet()) {
+ String path = fileEntry.getKey();
+ DynamicCodeFile fileInfo = fileEntry.getValue();
+ for (String loadingPackage : fileInfo.mLoadingPackages) {
+ entries.add(new Entry(owningPackage, path, fileInfo.mFileType, fileInfo.mUserId,
+ loadingPackage));
+ }
+ }
+ }
+
+ return entries.build();
+ }
+
+ private PackageDynamicCodeLoading makePackageDcl(Entry... entries) {
+ PackageDynamicCodeLoading result = new PackageDynamicCodeLoading();
+ for (Entry entry : entries) {
+ result.record(entry.mOwningPackage, entry.mPath, entry.mFileType, entry.mUserId,
+ entry.mLoadingPackage);
+ }
+ return result;
+
+ }
+
+ private void assertHasEntries(PackageDynamicCodeLoading info, Entry... expected) {
+ assertEquals(ImmutableSet.copyOf(expected), entriesFrom(info));
+ }
+
+ private void assertHasPackages(PackageDynamicCodeLoading info, String... expected) {
+ assertEquals(ImmutableSet.copyOf(expected), info.getAllPackagesWithDynamicCodeLoading());
+ }
+
+ /**
+ * Immutable representation of one entry in the dynamic code loading data (one package
+ * owning one file loaded by one package). Has well-behaved equality, hash and toString
+ * for ease of use in assertions.
+ */
+ private static class Entry {
+ private final String mOwningPackage;
+ private final String mPath;
+ private final char mFileType;
+ private final int mUserId;
+ private final String mLoadingPackage;
+
+ private Entry(String owningPackage, String path, char fileType, int userId,
+ String loadingPackage) {
+ mOwningPackage = owningPackage;
+ mPath = path;
+ mFileType = fileType;
+ mUserId = userId;
+ mLoadingPackage = loadingPackage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Entry that = (Entry) o;
+ return mFileType == that.mFileType
+ && mUserId == that.mUserId
+ && Objects.equals(mOwningPackage, that.mOwningPackage)
+ && Objects.equals(mPath, that.mPath)
+ && Objects.equals(mLoadingPackage, that.mLoadingPackage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mOwningPackage, mPath, mFileType, mUserId, mLoadingPackage);
+ }
+
+ @Override
+ public String toString() {
+ return "Entry("
+ + "\"" + mOwningPackage + '"'
+ + ", \"" + mPath + '"'
+ + ", '" + mFileType + '\''
+ + ", " + mUserId
+ + ", \"" + mLoadingPackage + '\"'
+ + ')';
+ }
+ }
+
+ private static class ThrowingOutputStream extends OutputStream {
+ @Override
+ public void write(int b) throws IOException {
+ throw new IOException("Intentional failure");
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 77f6a23..acf511e 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -20,7 +20,6 @@
import static org.mockito.Mockito.mock;
import android.content.Context;
-import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
@@ -175,7 +174,7 @@
@SmallTest
public void testGetBatterySaverPolicy_PolicyQuickDoze_DefaultValueCorrect() {
- testServiceDefaultValue_Off(ServiceType.QUICK_DOZE);
+ testServiceDefaultValue_On(ServiceType.QUICK_DOZE);
}
@SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 8ac2930..561c61f 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -37,6 +37,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.app.IBatteryStats;
@@ -193,4 +194,19 @@
assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
}
+
+ @MediumTest
+ public void testWasDeviceIdleFor_true() {
+ int interval = 1000;
+ mService.onUserActivity();
+ SystemClock.sleep(interval);
+ assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
+ }
+
+ @SmallTest
+ public void testWasDeviceIdleFor_false() {
+ int interval = 1000;
+ mService.onUserActivity();
+ assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 7cf7df13..94d293e 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -268,10 +268,10 @@
@Test
public void testGetCurrentStatus() throws RemoteException {
- int status = Temperature.THROTTLING_WARNING;
+ int status = Temperature.THROTTLING_EMERGENCY;
Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
mFakeHal.mCallback.onValues(newSkin);
- assertEquals(status, mService.mService.getCurrentStatus());
+ assertEquals(status, mService.mService.getCurrentThermalStatus());
}
@Test
@@ -281,6 +281,10 @@
mFakeHal.mCallback.onValues(newSkin);
verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+ Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.mCallback.onValues(newBattery);
+ verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
}
@Test
@@ -294,6 +298,6 @@
assertEquals(0, mService.mService.getCurrentTemperatures().size());
assertEquals(0,
mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
- assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+ assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
new file mode 100644
index 0000000..00a62a3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.util.ArrayMap;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.collect.Sets;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Tests for {@link SignedConfig}
+ */
+@RunWith(AndroidJUnit4.class)
+public class SignedConfigTest {
+
+ private static Set<String> setOf(String... values) {
+ return Sets.newHashSet(values);
+ }
+
+ private static <K, V> Map<K, V> mapOf(Object... keyValuePairs) {
+ if (keyValuePairs.length % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ final int len = keyValuePairs.length / 2;
+ ArrayMap<K, V> m = new ArrayMap<>(len);
+ for (int i = 0; i < len; ++i) {
+ m.put((K) keyValuePairs[i * 2], (V) keyValuePairs[(i * 2) + 1]);
+ }
+ return Collections.unmodifiableMap(m);
+
+ }
+
+
+ @Test
+ public void testParsePerSdkConfigSdkMinMax() throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject("{\"min_sdk\":2, \"max_sdk\": 3, \"values\": {}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, emptySet(),
+ emptyMap());
+ assertThat(config.minSdk).isEqualTo(2);
+ assertThat(config.maxSdk).isEqualTo(3);
+ }
+
+ @Test
+ public void testParsePerSdkConfigNoMinSdk() throws JSONException {
+ JSONObject json = new JSONObject("{\"max_sdk\": 3, \"values\": {}}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigNoMaxSdk() throws JSONException {
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"values\": {}}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigNoValues() throws JSONException {
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 3}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigSdkNullMinSdk() throws JSONException {
+ JSONObject json = new JSONObject("{\"min_sdk\":null, \"max_sdk\": 3, \"values\": {}}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigSdkNullMaxSdk() throws JSONException {
+ JSONObject json = new JSONObject("{\"min_sdk\":1, \"max_sdk\": null, \"values\": {}}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigNullValues() throws JSONException {
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 3, \"values\": null}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigZeroValues()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 3, \"values\": {}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ emptyMap());
+ assertThat(config.values).hasSize(0);
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKey()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ emptyMap());
+ assertThat(config.values).containsExactly("a", "1");
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyNullValue()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": null}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ emptyMap());
+ assertThat(config.values.keySet()).containsExactly("a");
+ assertThat(config.values.get("a")).isNull();
+ }
+
+ @Test
+ public void testParsePerSdkConfigMultiKeys()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\", \"c\": \"2\"}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(
+ json, setOf("a", "b", "c"), emptyMap());
+ assertThat(config.values).containsExactly("a", "1", "c", "2");
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyNotAllowed() throws JSONException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, setOf("b"), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyWithMap()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
+ mapOf("a", mapOf("1", "one")));
+ assertThat(config.values).containsExactly("a", "one");
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyWithMapInvalidValue() throws JSONException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"2\"}}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, setOf("b"), mapOf("a", mapOf("1", "one")));
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigMultiKeysWithMap()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\", \"b\": \"1\"}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ mapOf("a", mapOf("1", "one")));
+ assertThat(config.values).containsExactly("a", "one", "b", "1");
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyWithMapToNull()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
+ mapOf("a", mapOf("1", null)));
+ assertThat(config.values).containsExactly("a", null);
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyWithMapFromNull()
+ throws JSONException, InvalidConfigException {
+ assertThat(mapOf(null, "allitnil")).containsExactly(null, "allitnil");
+ assertThat(mapOf(null, "allitnil").containsKey(null)).isTrue();
+ JSONObject json = new JSONObject(
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": null}}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
+ mapOf("a", mapOf(null, "allitnil")));
+ assertThat(config.values).containsExactly("a", "allitnil");
+ }
+
+ @Test
+ public void testParsePerSdkConfigValuesInvalid() throws JSONException {
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": \"foo\"}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigConfigEntryInvalid() throws JSONException {
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": [1, 2]}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParseVersion() throws InvalidConfigException {
+ SignedConfig config = SignedConfig.parse(
+ "{\"version\": 1, \"config\": []}", emptySet(), emptyMap());
+ assertThat(config.version).isEqualTo(1);
+ }
+
+ @Test
+ public void testParseVersionInvalid() {
+ try {
+ SignedConfig.parse("{\"version\": \"notanint\", \"config\": []}", emptySet(),
+ emptyMap());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseNoVersion() {
+ try {
+ SignedConfig.parse("{\"config\": []}", emptySet(), emptyMap());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseNoConfig() {
+ try {
+ SignedConfig.parse("{\"version\": 1}", emptySet(), emptyMap());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseConfigNull() {
+ try {
+ SignedConfig.parse("{\"version\": 1, \"config\": null}", emptySet(), emptyMap());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseVersionNull() {
+ try {
+ SignedConfig.parse("{\"version\": null, \"config\": []}", emptySet(), emptyMap());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseConfigInvalidEntry() {
+ try {
+ SignedConfig.parse("{\"version\": 1, \"config\": [{}]}", emptySet(), emptyMap());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseSdkConfigSingle() throws InvalidConfigException {
+ SignedConfig config = SignedConfig.parse(
+ "{\"version\": 1, \"config\":[{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {}}]}",
+ emptySet(), emptyMap());
+ assertThat(config.perSdkConfig).hasSize(1);
+ }
+
+ @Test
+ public void testParseSdkConfigMultiple() throws InvalidConfigException {
+ SignedConfig config = SignedConfig.parse(
+ "{\"version\": 1, \"config\":[{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {}}, "
+ + "{\"min_sdk\": 2, \"max_sdk\": 2, \"values\": {}}]}", emptySet(),
+ emptyMap());
+ assertThat(config.perSdkConfig).hasSize(2);
+ }
+
+ @Test
+ public void testGetMatchingConfigFirst() {
+ SignedConfig.PerSdkConfig sdk1 = new SignedConfig.PerSdkConfig(
+ 1, 1, Collections.emptyMap());
+ SignedConfig.PerSdkConfig sdk2 = new SignedConfig.PerSdkConfig(
+ 2, 2, Collections.emptyMap());
+ SignedConfig config = new SignedConfig(0, Arrays.asList(sdk1, sdk2));
+ assertThat(config.getMatchingConfig(1)).isEqualTo(sdk1);
+ }
+
+ @Test
+ public void testGetMatchingConfigSecond() {
+ SignedConfig.PerSdkConfig sdk1 = new SignedConfig.PerSdkConfig(
+ 1, 1, Collections.emptyMap());
+ SignedConfig.PerSdkConfig sdk2 = new SignedConfig.PerSdkConfig(
+ 2, 2, Collections.emptyMap());
+ SignedConfig config = new SignedConfig(0, Arrays.asList(sdk1, sdk2));
+ assertThat(config.getMatchingConfig(2)).isEqualTo(sdk2);
+ }
+
+ @Test
+ public void testGetMatchingConfigInRange() {
+ SignedConfig.PerSdkConfig sdk13 = new SignedConfig.PerSdkConfig(
+ 1, 3, Collections.emptyMap());
+ SignedConfig.PerSdkConfig sdk46 = new SignedConfig.PerSdkConfig(
+ 4, 6, Collections.emptyMap());
+ SignedConfig config = new SignedConfig(0, Arrays.asList(sdk13, sdk46));
+ assertThat(config.getMatchingConfig(2)).isEqualTo(sdk13);
+ }
+ @Test
+ public void testGetMatchingConfigNoMatch() {
+ SignedConfig.PerSdkConfig sdk1 = new SignedConfig.PerSdkConfig(
+ 1, 1, Collections.emptyMap());
+ SignedConfig.PerSdkConfig sdk2 = new SignedConfig.PerSdkConfig(
+ 2, 2, Collections.emptyMap());
+ SignedConfig config = new SignedConfig(0, Arrays.asList(sdk1, sdk2));
+ assertThat(config.getMatchingConfig(3)).isNull();
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index bf7f53d..860656b 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -16,6 +16,8 @@
package com.android.server.usage;
+import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
+
import static junit.framework.TestCase.fail;
import static org.testng.Assert.assertEquals;
@@ -136,9 +138,11 @@
event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
}
- event.mClass = ".fake.class.name" + i % 11;
+ final int instanceId = i % 11;
+ event.mClass = ".fake.class.name" + instanceId;
event.mTimeStamp = time;
- event.mEventType = i % 19; //"random" event type
+ event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
+ event.mInstanceId = instanceId;
switch (event.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -160,7 +164,8 @@
}
mIntervalStats.events.insert(event);
- mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType);
+ mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
+ event.mInstanceId);
time += timeProgression; // Arbitrary progression of time
}
@@ -219,7 +224,9 @@
// mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking
// mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+ assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+ assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
// mLaunchCount not persisted, so skipped
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index ca8cc0d..7a5eaa8 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -50,9 +50,9 @@
"liblog",
"liblzma",
"libnativehelper",
- "libnetdaidl",
"libui",
"libunwindstack",
"libutils",
+ "netd_aidl_interface-cpp",
],
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9da204f..41d5a1c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -456,6 +456,31 @@
}
@Test
+ public void testNoBeepForImportanceDefaultInAutomotive() throws Exception {
+ mService.setIsAutomotive(true);
+
+ NotificationRecord r = getBeepyNotification();
+ r.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testBeepForImportanceHighInAutomotive() throws Exception {
+ mService.setIsAutomotive(true);
+
+ NotificationRecord r = getBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyBeepLooped();
+ assertTrue(r.isInterruptive());
+ }
+
+ @Test
public void testNoInterruptionForMin() throws Exception {
NotificationRecord r = getBeepyNotification();
r.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index b30bb4b..0681295 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -15,8 +15,9 @@
*/
package com.android.server.notification;
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -34,8 +35,8 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.telecom.TelecomManager;
import android.support.test.runner.AndroidJUnit4;
+import android.telecom.TelecomManager;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.UiServiceTestCase;
@@ -211,7 +212,7 @@
mRecordColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "colorized", uid2, uid2, n13,
new UserHandle(userId), "", 1999), getDefaultChannel());
- mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
+ mRecordColorized.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n14 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
@@ -225,11 +226,11 @@
}
@Test
- public void testOrdering() throws Exception {
+ public void testOrdering() {
final List<NotificationRecord> expected = new ArrayList<>();
expected.add(mRecordColorizedCall);
- expected.add(mRecordDefaultMedia);
expected.add(mRecordColorized);
+ expected.add(mRecordDefaultMedia);
expected.add(mRecordHighCall);
expected.add(mRecordInlineReply);
if (mRecordSms != null) {
@@ -250,11 +251,11 @@
Collections.sort(actual, new NotificationComparator(mContext));
- assertEquals(expected, actual);
+ assertThat(actual, contains(expected.toArray()));
}
@Test
- public void testMessaging() throws Exception {
+ public void testMessaging() {
NotificationComparator comp = new NotificationComparator(mContext);
assertTrue(comp.isImportantMessaging(mRecordInlineReply));
if (mRecordSms != null) {
@@ -265,7 +266,7 @@
}
@Test
- public void testPeople() throws Exception {
+ public void testPeople() {
NotificationComparator comp = new NotificationComparator(mContext);
assertTrue(comp.isImportantPeople(mRecordStarredContact));
assertTrue(comp.isImportantPeople(mRecordContact));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d950360..527a1ee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
@@ -183,7 +184,7 @@
Resources mResources;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
@Mock
private NotificationListeners mListeners;
@Mock private NotificationAssistants mAssistants;
@@ -238,6 +239,11 @@
protected void reportUserInteraction(NotificationRecord r) {
return;
}
+
+ @Override
+ protected void handleSavePolicyFile() {
+ return;
+ }
}
private class TestableToastCallback extends ITransientNotification.Stub {
@@ -425,7 +431,7 @@
@Test
public void testCreateNotificationChannels_SingleChannel() throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
final NotificationChannel createdChannel =
@@ -447,9 +453,9 @@
@Test
public void testCreateNotificationChannels_TwoChannels() throws Exception {
final NotificationChannel channel1 =
- new NotificationChannel("id1", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id1", "name", IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
- new NotificationChannel("id2", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id2", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel1, channel2)));
assertTrue(mBinderService.getNotificationChannel(PKG, "id1") != null);
@@ -460,7 +466,7 @@
public void testCreateNotificationChannels_SecondCreateDoesNotChangeImportance()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -471,14 +477,14 @@
new ParceledListSlice(Arrays.asList(dupeChannel)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel(PKG, "id");
- assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
public void testCreateNotificationChannels_SecondCreateAllowedToDowngradeImportance()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -496,7 +502,7 @@
public void testCreateNotificationChannels_CannotDowngradeImportanceIfAlreadyUpdated()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -519,14 +525,14 @@
public void testCreateNotificationChannels_IdenticalChannelsInListIgnoresSecond()
throws Exception {
final NotificationChannel channel1 =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
new NotificationChannel("id", "name", IMPORTANCE_HIGH);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel1, channel2)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel(PKG, "id");
- assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
@@ -751,13 +757,18 @@
@Test
public void testBlockedNotifications_blockedByAssistant() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
mService.addEnqueuedNotification(r);
- r.setAssistantImportance(IMPORTANCE_NONE);
+ Bundle bundle = new Bundle();
+ bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(r.getKey());
@@ -1809,7 +1820,6 @@
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
-
try {
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
@@ -2613,7 +2623,7 @@
}
@Test
- public void testAssistantIBlockingTriggersCancel() throws Exception {
+ public void testAssistantBlockingTriggersCancel() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
NotificationManagerService.WorkerHandler handler = mock(
@@ -2653,6 +2663,43 @@
}
@Test
+ public void testApplyEnqueuedAdjustmentFromAssistant_importance_onTime() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ assertEquals(IMPORTANCE_LOW, r.getImportance());
+ }
+
+ @Test
+ public void testApplyEnqueuedAdjustmentFromAssistant_importance_tooLate() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ assertEquals(IMPORTANCE_DEFAULT, r.getImportance());
+ assertFalse(r.hasAdjustment(Adjustment.KEY_IMPORTANCE));
+ }
+
+ @Test
public void testApplyEnqueuedAdjustmentFromAssistant_crossUser() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addEnqueuedNotification(r);
@@ -2852,7 +2899,7 @@
@Test
public void updateUriPermissions_update() throws Exception {
NotificationChannel c = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
Message message1 = new Message("", 0, "");
message1.setData("",
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 8690110..0eeeeed 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -760,6 +760,7 @@
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(IMPORTANCE_LOW, record.getImportance());
// assistant ignored if user expressed preference
@@ -767,6 +768,7 @@
channel.lockFields(USER_LOCKED_IMPORTANCE);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(channel.getImportance(), record.getImportance());
}
@@ -779,6 +781,7 @@
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(IMPORTANCE_LOW, record.getImportance());
record.updateNotificationChannel(
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index 82e0fbe..a71aca5 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -106,8 +106,8 @@
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
- argThat(b -> {
+ verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
+ eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 048384e..85410f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -82,17 +82,17 @@
@Test
public void testEmptyTaskCleanupOnRemove() {
- assertNotNull(mTask.getWindowContainerController());
+ assertNotNull(mTask.getTask());
mStack.removeTask(mTask, "testEmptyTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
- assertNull(mTask.getWindowContainerController());
+ assertNull(mTask.getTask());
}
@Test
public void testOccupiedTaskCleanupOnRemove() {
final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
- assertNotNull(mTask.getWindowContainerController());
+ assertNotNull(mTask.getTask());
mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
- assertNotNull(mTask.getWindowContainerController());
+ assertNotNull(mTask.getTask());
}
@Test
@@ -162,7 +162,8 @@
assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
// Ensure that the override mode is restored to undefined
- assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+ assertEquals(WINDOWING_MODE_UNDEFINED,
+ primarySplitScreen.getRequestedOverrideWindowingMode());
}
@Test
@@ -184,7 +185,8 @@
assertEquals(0, mDefaultDisplay.getIndexOf(primarySplitScreen));
// Ensure that the override mode is restored to what it was (fullscreen)
- assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getOverrideWindowingMode());
+ assertEquals(WINDOWING_MODE_FULLSCREEN,
+ primarySplitScreen.getRequestedOverrideWindowingMode());
}
@Test
@@ -193,11 +195,13 @@
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
- assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+ assertEquals(WINDOWING_MODE_UNDEFINED,
+ primarySplitScreen.getRequestedOverrideWindowingMode());
mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(WINDOWING_MODE_FREEFORM, primarySplitScreen.getWindowingMode());
- assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+ assertEquals(WINDOWING_MODE_UNDEFINED,
+ primarySplitScreen.getRequestedOverrideWindowingMode());
}
@Test
@@ -206,11 +210,13 @@
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
- assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+ assertEquals(WINDOWING_MODE_UNDEFINED,
+ primarySplitScreen.getRequestedOverrideWindowingMode());
primarySplitScreen.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
// setting windowing mode should still work even though resolved mode is already fullscreen
- assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getOverrideWindowingMode());
+ assertEquals(WINDOWING_MODE_FULLSCREEN,
+ primarySplitScreen.getRequestedOverrideWindowingMode());
mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 5c918b4..7c43cf3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -135,8 +135,8 @@
final Rect bounds = new Rect(10, 10, 100, 100);
mStarter.updateBounds(task, bounds);
- assertEquals(bounds, task.getOverrideBounds());
- assertEquals(new Rect(), task.getStack().getOverrideBounds());
+ assertEquals(bounds, task.getRequestedOverrideBounds());
+ assertEquals(new Rect(), task.getStack().getRequestedOverrideBounds());
// When in a resizeable stack, the stack bounds should be updated as well.
final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
@@ -151,10 +151,10 @@
// In the case of no animation, the stack and task bounds should be set immediately.
if (!ANIMATE) {
- assertEquals(bounds, task2.getStack().getOverrideBounds());
- assertEquals(bounds, task2.getOverrideBounds());
+ assertEquals(bounds, task2.getStack().getRequestedOverrideBounds());
+ assertEquals(bounds, task2.getRequestedOverrideBounds());
} else {
- assertEquals(new Rect(), task2.getOverrideBounds());
+ assertEquals(new Rect(), task2.getRequestedOverrideBounds());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 3a56419..569c6d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -345,7 +345,7 @@
mStack.moveToFront("test");
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
- task.setWindowContainerController();
+ task.setTask();
}
task.touchActiveTime();
@@ -361,12 +361,12 @@
}
@Override
- void createWindowContainer(boolean onTop, boolean showForAllUsers) {
- setWindowContainerController();
+ void createTask(boolean onTop, boolean showForAllUsers) {
+ setTask();
}
- private void setWindowContainerController() {
- setWindowContainerController(mock(TaskWindowContainerController.class));
+ private void setTask() {
+ setTask(mock(Task.class));
}
}
}
@@ -408,6 +408,7 @@
initialize(intentFirewall, intentController, looper);
initRootActivityContainerMocks(wm);
setWindowManager(wm);
+ createDefaultDisplay();
}
void initRootActivityContainerMocks(WindowManagerService wm) {
@@ -424,7 +425,9 @@
// Called when moving activity to pinned stack.
doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
anyBoolean());
+ }
+ void createDefaultDisplay() {
// Create a default display and put a home stack on it so that we'll always have
// something focusable.
mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
@@ -444,7 +447,11 @@
}
@Override
- void updateUsageStats(ActivityRecord component, boolean resumed) {
+ void updateBatteryStats(ActivityRecord component, boolean resumed) {
+ }
+
+ @Override
+ void updateActivityUsageStats(ActivityRecord activity, int event) {
}
@Override
@@ -598,10 +605,8 @@
}
@Override
- protected DisplayWindowController createWindowContainerController() {
- DisplayWindowController out = mock(DisplayWindowController.class);
- out.mContainer = WindowTestUtils.createTestDisplayContent();
- return out;
+ protected DisplayContent createDisplayContent() {
+ return WindowTestUtils.createTestDisplayContent();
}
void removeAllTasks() {
@@ -616,6 +621,7 @@
private static WindowManagerService prepareMockWindowManager() {
final WindowManagerService service = mock(WindowManagerService.class);
+ service.mRoot = mock(RootWindowContainer.class);
doAnswer((InvocationOnMock invocationOnMock) -> {
final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 577859c..fa42289 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -80,7 +80,7 @@
@Test
public void testForceOverride() {
mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
- mDc.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+ mDc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition());
}
@@ -102,8 +102,8 @@
@Test
public void testAppTransitionStateForMultiDisplay() {
// Create 2 displays & presume both display the state is ON for ready to display & animate.
- final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
- final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
+ final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
+ final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
// Create 2 app window tokens to represent 2 activity window.
final WindowTestUtils.TestAppWindowToken token1 = createTestAppWindowToken(dc1,
@@ -117,10 +117,10 @@
// Simulate activity resume / finish flows to prepare app transition & set visibility,
// make sure transition is set as expected for each display.
- dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+ dc1.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
- dc2.getController().prepareAppTransition(TRANSIT_ACTIVITY_CLOSE,
+ dc2.prepareAppTransition(TRANSIT_ACTIVITY_CLOSE,
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
// One activity window is visible for resuming & the other activity window is invisible
@@ -138,8 +138,8 @@
@Test
public void testCleanAppTransitionWhenTaskStackReparent() {
// Create 2 displays & presume both display the state is ON for ready to display & animate.
- final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
- final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
+ final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
+ final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
final TaskStack stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
@@ -151,7 +151,7 @@
dc1.mClosingApps.add(token1);
assertTrue(dc1.mClosingApps.size() > 0);
- dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+ dc1.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
assertTrue(dc1.mAppTransition.isTransitionSet());
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 0ee532d..92b4dbb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -155,15 +155,15 @@
// Set initial orientation and update.
mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
- mDisplayContent.getDisplayId());
+ mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
+ null, mDisplayContent.getDisplayId());
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
appWindow.mResizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
- mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
- mDisplayContent.getDisplayId());
+ mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
+ null, mDisplayContent.getDisplayId());
mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
assertTrue(appWindow.mResizeReported);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
index 82a200b..e7f7d21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -27,6 +27,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
import static android.content.res.Configuration.EMPTY;
+import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -55,22 +56,22 @@
public void testConfigurationInit() {
// Check root container initial config.
final TestConfigurationContainer root = new TestConfigurationContainer();
- assertEquals(EMPTY, root.getOverrideConfiguration());
+ assertEquals(EMPTY, root.getRequestedOverrideConfiguration());
assertEquals(EMPTY, root.getMergedOverrideConfiguration());
assertEquals(EMPTY, root.getConfiguration());
// Check child initial config.
final TestConfigurationContainer child1 = root.addChild();
- assertEquals(EMPTY, child1.getOverrideConfiguration());
+ assertEquals(EMPTY, child1.getRequestedOverrideConfiguration());
assertEquals(EMPTY, child1.getMergedOverrideConfiguration());
assertEquals(EMPTY, child1.getConfiguration());
// Check child initial config if root has overrides.
final Configuration rootOverrideConfig = new Configuration();
rootOverrideConfig.fontScale = 1.3f;
- root.onOverrideConfigurationChanged(rootOverrideConfig);
+ root.onRequestedOverrideConfigurationChanged(rootOverrideConfig);
final TestConfigurationContainer child2 = root.addChild();
- assertEquals(EMPTY, child2.getOverrideConfiguration());
+ assertEquals(EMPTY, child2.getRequestedOverrideConfiguration());
assertEquals(rootOverrideConfig, child2.getMergedOverrideConfiguration());
assertEquals(rootOverrideConfig, child2.getConfiguration());
@@ -83,7 +84,7 @@
rootFullConfig.updateFrom(rootOverrideConfig);
final TestConfigurationContainer child3 = root.addChild();
- assertEquals(EMPTY, child3.getOverrideConfiguration());
+ assertEquals(EMPTY, child3.getRequestedOverrideConfiguration());
assertEquals(rootOverrideConfig, child3.getMergedOverrideConfiguration());
assertEquals(rootFullConfig, child3.getConfiguration());
}
@@ -94,24 +95,24 @@
final TestConfigurationContainer root = new TestConfigurationContainer();
final Configuration rootOverrideConfig = new Configuration();
rootOverrideConfig.fontScale = 1.3f;
- root.onOverrideConfigurationChanged(rootOverrideConfig);
+ root.onRequestedOverrideConfigurationChanged(rootOverrideConfig);
// Init child's config.
final TestConfigurationContainer child = root.addChild();
final Configuration childOverrideConfig = new Configuration();
childOverrideConfig.densityDpi = 320;
- child.onOverrideConfigurationChanged(childOverrideConfig);
+ child.onRequestedOverrideConfigurationChanged(childOverrideConfig);
final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
mergedOverrideConfig.updateFrom(childOverrideConfig);
// Check configuration update when child is removed from parent.
root.removeChild(child);
- assertEquals(childOverrideConfig, child.getOverrideConfiguration());
+ assertEquals(childOverrideConfig, child.getRequestedOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getConfiguration());
// It may be paranoia... but let's check if parent's config didn't change after removal.
- assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+ assertEquals(rootOverrideConfig, root.getRequestedOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getConfiguration());
@@ -119,13 +120,13 @@
final TestConfigurationContainer root2 = new TestConfigurationContainer();
final Configuration rootOverrideConfig2 = new Configuration();
rootOverrideConfig2.fontScale = 1.1f;
- root2.onOverrideConfigurationChanged(rootOverrideConfig2);
+ root2.onRequestedOverrideConfigurationChanged(rootOverrideConfig2);
// Check configuration update when child is added to different parent.
mergedOverrideConfig.setTo(rootOverrideConfig2);
mergedOverrideConfig.updateFrom(childOverrideConfig);
root2.addChild(child);
- assertEquals(childOverrideConfig, child.getOverrideConfiguration());
+ assertEquals(childOverrideConfig, child.getRequestedOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getConfiguration());
}
@@ -141,24 +142,24 @@
final Configuration rootOverrideConfig = new Configuration();
rootOverrideConfig.fontScale = 1.3f;
rootOverrideConfig.orientation = SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
- root.onOverrideConfigurationChanged(rootOverrideConfig);
+ root.onRequestedOverrideConfigurationChanged(rootOverrideConfig);
// Init children.
final TestConfigurationContainer child1 = root.addChild();
final Configuration childOverrideConfig1 = new Configuration();
childOverrideConfig1.densityDpi = 320;
childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE;
- child1.onOverrideConfigurationChanged(childOverrideConfig1);
+ child1.onRequestedOverrideConfigurationChanged(childOverrideConfig1);
final TestConfigurationContainer child2 = child1.addChild();
final Configuration childOverrideConfig2 = new Configuration();
childOverrideConfig2.screenWidthDp = 150;
childOverrideConfig2.orientation = SCREEN_ORIENTATION_PORTRAIT;
- child2.onOverrideConfigurationChanged(childOverrideConfig2);
+ child2.onRequestedOverrideConfigurationChanged(childOverrideConfig2);
// Check configuration on all levels when root override is updated.
rootOverrideConfig.smallestScreenWidthDp = 200;
- root.onOverrideConfigurationChanged(rootOverrideConfig);
+ root.onRequestedOverrideConfigurationChanged(rootOverrideConfig);
final Configuration mergedOverrideConfig1 = new Configuration(rootOverrideConfig);
mergedOverrideConfig1.updateFrom(childOverrideConfig1);
@@ -168,15 +169,15 @@
mergedOverrideConfig2.updateFrom(childOverrideConfig2);
final Configuration mergedConfig2 = new Configuration(mergedOverrideConfig2);
- assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+ assertEquals(rootOverrideConfig, root.getRequestedOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getConfiguration());
- assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
+ assertEquals(childOverrideConfig1, child1.getRequestedOverrideConfiguration());
assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
assertEquals(mergedConfig1, child1.getConfiguration());
- assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
+ assertEquals(childOverrideConfig2, child2.getRequestedOverrideConfiguration());
assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
assertEquals(mergedConfig2, child2.getConfiguration());
@@ -194,15 +195,15 @@
mergedConfig2.setTo(mergedConfig1);
mergedConfig2.updateFrom(mergedOverrideConfig2);
- assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+ assertEquals(rootOverrideConfig, root.getRequestedOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
assertEquals(mergedRootConfig, root.getConfiguration());
- assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
+ assertEquals(childOverrideConfig1, child1.getRequestedOverrideConfiguration());
assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
assertEquals(mergedConfig1, child1.getConfiguration());
- assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
+ assertEquals(childOverrideConfig2, child2.getRequestedOverrideConfiguration());
assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
assertEquals(mergedConfig2, child2.getConfiguration());
}
@@ -274,15 +275,55 @@
final Configuration config = new Configuration();
config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
config.windowConfiguration.setAppBounds(10, 10, 10, 10);
- container.onOverrideConfigurationChanged(config);
+ container.onRequestedOverrideConfigurationChanged(config);
container.registerConfigurationChangeListener(listener);
// Assert listener got the current config. of the container after it was registered.
assertEquals(config, listener.mOverrideConfiguration);
// Assert listener gets changes to override configuration.
- container.onOverrideConfigurationChanged(EMPTY);
+ container.onRequestedOverrideConfigurationChanged(EMPTY);
assertEquals(EMPTY, listener.mOverrideConfiguration);
}
+ @Test
+ public void testConfigurationConstraints() {
+ // Init root config.
+ final TestConfigurationContainer root = new TestConfigurationContainer();
+ final Configuration rootOverrideConfig = new Configuration();
+ rootOverrideConfig.smallestScreenWidthDp = 140;
+ root.onRequestedOverrideConfigurationChanged(rootOverrideConfig);
+
+ // Init child with constraint
+ final TestConfigurationChangeListener listener = new TestConfigurationChangeListener();
+ final TestConfigurationContainer child1 = root.addConstraintChild();
+ child1.registerConfigurationChangeListener(listener);
+ final Configuration childOverrideConfig1 = new Configuration();
+ childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE;
+ child1.onRequestedOverrideConfigurationChanged(childOverrideConfig1);
+
+ assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED,
+ child1.getRequestedOverrideConfiguration().smallestScreenWidthDp);
+ assertEquals(100, child1.getConfiguration().smallestScreenWidthDp);
+ assertEquals(100, listener.mOverrideConfiguration.smallestScreenWidthDp);
+
+ // Check configuration on all levels when root override is updated.
+ rootOverrideConfig.smallestScreenWidthDp = 80;
+ root.onRequestedOverrideConfigurationChanged(rootOverrideConfig);
+
+ assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED,
+ child1.getRequestedOverrideConfiguration().smallestScreenWidthDp);
+ assertEquals(80, child1.getConfiguration().smallestScreenWidthDp);
+ assertEquals(80, listener.mOverrideConfiguration.smallestScreenWidthDp);
+
+ rootOverrideConfig.smallestScreenWidthDp = 180;
+ root.onRequestedOverrideConfigurationChanged(rootOverrideConfig);
+
+ assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED,
+ child1.getRequestedOverrideConfiguration().smallestScreenWidthDp);
+ assertEquals(100, child1.getConfiguration().smallestScreenWidthDp);
+ assertEquals(100, child1.getMergedOverrideConfiguration().smallestScreenWidthDp);
+ assertEquals(100, listener.mOverrideConfiguration.smallestScreenWidthDp);
+ }
+
/**
* Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
* for testing.
@@ -303,6 +344,10 @@
return addChild(new TestConfigurationContainer());
}
+ TestConfigurationContainer addConstraintChild() {
+ return addChild(new TestConfigurationContainerWithConstraints());
+ }
+
void removeChild(TestConfigurationContainer child) {
child.mParent = null;
child.onParentChanged();
@@ -324,12 +369,35 @@
}
}
+ /**
+ * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
+ * for testing.
+ */
+ private class TestConfigurationContainerWithConstraints
+ extends TestConfigurationContainer {
+
+ @Override
+ public void resolveOverrideConfiguration(Configuration newParentConfig) {
+ // Restrict smallestScreenWidthDp to 100
+ getResolvedOverrideConfiguration().setTo(getRequestedOverrideConfiguration());
+ int smallestScreenWidthDp =
+ getResolvedOverrideConfiguration().smallestScreenWidthDp
+ == SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
+ ? newParentConfig.smallestScreenWidthDp
+ : getResolvedOverrideConfiguration().smallestScreenWidthDp;
+ if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ getResolvedOverrideConfiguration().smallestScreenWidthDp =
+ Math.min(smallestScreenWidthDp, 100);
+ }
+ }
+ }
+
private static class TestConfigurationChangeListener implements ConfigurationContainerListener {
final Configuration mOverrideConfiguration = new Configuration();
@Override
- public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
mOverrideConfiguration.setTo(overrideConfiguration);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3b8d71d..7c83ecc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -227,7 +227,8 @@
*/
@Test
public void testDisplayOverrideConfigUpdate() {
- final Configuration currentOverrideConfig = mDisplayContent.getOverrideConfiguration();
+ final Configuration currentOverrideConfig =
+ mDisplayContent.getRequestedOverrideConfiguration();
// Create new, slightly changed override configuration and apply it to the display.
final Configuration newOverrideConfig = new Configuration(currentOverrideConfig);
@@ -237,7 +238,7 @@
mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, mDisplayContent);
// Check that override config is applied.
- assertEquals(newOverrideConfig, mDisplayContent.getOverrideConfiguration());
+ assertEquals(newOverrideConfig, mDisplayContent.getRequestedOverrideConfiguration());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index b94f472..845a09f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -95,52 +95,6 @@
}
@Test
- public void layoutWindowLw_appDrawsBars() {
- mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_appWontDrawBars() {
- mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
-
- @Test
- public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
- mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
-
- @Test
public void addingWindow_doesNotTamperWithSysuiFlags() {
mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
addWindow(mWindow);
@@ -150,253 +104,331 @@
}
@Test
+ public void layoutWindowLw_appDrawsBars() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_appWontDrawBars() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+ }
+ }
+
+ @Test
public void layoutWindowLw_withDisplayCutout() {
- addDisplayCutout();
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
- addWindow(mWindow);
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_never() {
- addDisplayCutout();
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
- addDisplayCutout();
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- addWindow(mWindow);
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreen() {
- addDisplayCutout();
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- addWindow(mWindow);
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
- addDisplayCutout();
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
- addWindow(mWindow);
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_seascape() {
- addDisplayCutout();
- setRotation(ROTATION_270);
- addWindow(mWindow);
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_270);
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- addWindow(mWindow);
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
- addDisplayCutout();
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
- mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
- mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
- mWindow.mAttrs.width = DISPLAY_WIDTH;
- mWindow.mAttrs.height = DISPLAY_HEIGHT;
- addWindow(mWindow);
+ mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
+ mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
+ mWindow.mAttrs.width = DISPLAY_WIDTH;
+ mWindow.mAttrs.height = DISPLAY_HEIGHT;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ }
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ }
}
@Test
public void layoutHint_appWindow() {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ synchronized (mWm.mGlobalLock) {
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
- false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
- outDisplayCutout);
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
+ false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
- assertThat(outFrame, is(mFrames.mUnrestricted));
- assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
- assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ assertThat(outFrame, is(mFrames.mUnrestricted));
+ assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+ assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ }
}
@Test
public void layoutHint_appWindowInTask() {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ synchronized (mWm.mGlobalLock) {
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- final Rect taskBounds = new Rect(100, 100, 200, 200);
+ final Rect taskBounds = new Rect(100, 100, 200, 200);
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
- false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
- outDisplayCutout);
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+ false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
- assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ assertThat(outFrame, is(taskBounds));
+ assertThat(outContentInsets, is(new Rect()));
+ assertThat(outStableInsets, is(new Rect()));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ }
}
@Test
public void layoutHint_appWindowInTask_outsideContentFrame() {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ synchronized (mWm.mGlobalLock) {
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- // Task is in the nav bar area (usually does not happen, but this is similar enough to the
- // possible overlap with the IME)
- final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
- 200, mFrames.mContent.bottom + 10);
+ // Task is in the nav bar area (usually does not happen, but this is similar enough to
+ // the possible overlap with the IME)
+ final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+ 200, mFrames.mContent.bottom + 10);
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
- true /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
- outDisplayCutout);
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+ true /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
- assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ assertThat(outFrame, is(taskBounds));
+ assertThat(outContentInsets, is(new Rect()));
+ assertThat(outStableInsets, is(new Rect()));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 1d63c57..7be331c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -79,6 +79,7 @@
resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
when(mDisplayPolicy.getSystemUiContext()).thenReturn(context);
when(mDisplayPolicy.hasNavigationBar()).thenReturn(true);
+ when(mDisplayPolicy.hasStatusBar()).thenReturn(true);
final int shortSizeDp =
Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 3c7b4b1..3e025f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -875,8 +875,8 @@
}
@Override
- void initRootActivityContainerMocks(WindowManagerService wm) {
- super.initRootActivityContainerMocks(wm);
+ void createDefaultDisplay() {
+ super.createDefaultDisplay();
mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
index ce5b13c..5690b58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -41,17 +41,14 @@
public void testRemoveContainer() {
final StackWindowController stackController =
createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
final TaskStack stack = stackController.mContainer;
- final Task task = taskController.mContainer;
assertNotNull(stack);
assertNotNull(task);
stackController.removeContainer();
// Assert that the container was removed.
assertNull(stackController.mContainer);
- assertNull(taskController.mContainer);
assertNull(stack.getDisplayContent());
assertNull(task.getDisplayContent());
assertNull(task.mStack);
@@ -61,11 +58,9 @@
public void testRemoveContainer_deferRemoval() {
final StackWindowController stackController =
createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
final TaskStack stack = stackController.mContainer;
- final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer;
// Stack removal is deferred if one of its child is animating.
task.setLocalIsAnimating(true);
@@ -75,11 +70,12 @@
// the stack window container is removed.
assertNull(stackController.mContainer);
assertNull(stack.getController());
- assertNotNull(taskController.mContainer);
- assertNotNull(task.getController());
+ assertNotNull(task);
stack.removeImmediately();
- assertNull(taskController.mContainer);
+ // After removing, the task will be isolated.
+ assertNull(task.getParent());
+ assertEquals(task.getChildCount(), 0);
assertNull(task.getController());
}
@@ -89,9 +85,7 @@
final StackWindowController stack1Controller =
createStackControllerOnDisplay(mDisplayContent);
final TaskStack stack1 = stack1Controller.mContainer;
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stack1Controller);
- final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer;
+ final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1Controller);
task1.mOnDisplayChangedCalled = false;
// Create second display and put second stack on it.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 9569c0d5..fe632d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -22,7 +22,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -750,6 +753,64 @@
}
@Test
+ public void testUsesDisplayOrientationForNoSensorOrientation() {
+ final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+ WINDOWING_MODE_FREEFORM);
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ mActivity.info.screenOrientation = SCREEN_ORIENTATION_NOSENSOR;
+
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+ mActivity, /* source */ null, options, mCurrent, mResult));
+
+ final int orientationForDisplay = orientationFromBounds(freeformDisplay.getBounds());
+ final int orientationForTask = orientationFromBounds(mResult.mBounds);
+ assertEquals("Launch bounds orientation should be the same as the display, but"
+ + " display orientation is "
+ + ActivityInfo.screenOrientationToString(orientationForDisplay)
+ + " launch bounds orientation is "
+ + ActivityInfo.screenOrientationToString(orientationForTask),
+ orientationForDisplay, orientationForTask);
+ }
+
+ @Test
+ public void testRespectsAppRequestedOrientation_Landscape() {
+ final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+ WINDOWING_MODE_FREEFORM);
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+ mActivity, /* source */ null, options, mCurrent, mResult));
+
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, orientationFromBounds(mResult.mBounds));
+ }
+
+ @Test
+ public void testRespectsAppRequestedOrientation_Portrait() {
+ final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+ WINDOWING_MODE_FREEFORM);
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+ mActivity, /* source */ null, options, mCurrent, mResult));
+
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, orientationFromBounds(mResult.mBounds));
+ }
+
+ @Test
public void testDefaultSizeSmallerThanBigScreen() {
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
@@ -888,10 +949,11 @@
@Test
public void testAdjustBoundsToFitNewDisplay_LargerThanDisplay_RTL() {
- final Configuration overrideConfig = mRootActivityContainer.getOverrideConfiguration();
+ final Configuration overrideConfig =
+ mRootActivityContainer.getRequestedOverrideConfiguration();
// Egyptian Arabic is a RTL language.
overrideConfig.setLayoutDirection(new Locale("ar", "EG"));
- mRootActivityContainer.onOverrideConfigurationChanged(overrideConfig);
+ mRootActivityContainer.onRequestedOverrideConfigurationChanged(overrideConfig);
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
@@ -1090,6 +1152,7 @@
display.setWindowingMode(windowingMode);
display.setBounds(/* left */ 0, /* top */ 0, /* right */ 1920, /* bottom */ 1080);
display.getConfiguration().densityDpi = DENSITY_DEFAULT;
+ display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
return display;
}
@@ -1115,6 +1178,11 @@
}
}
+ private int orientationFromBounds(Rect bounds) {
+ return bounds.width() > bounds.height() ? SCREEN_ORIENTATION_LANDSCAPE
+ : SCREEN_ORIENTATION_PORTRAIT;
+ }
+
private static class WindowLayoutBuilder {
private int mWidth = -1;
private int mHeight = -1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 630a8bf..6b6b33c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
@@ -31,10 +34,12 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.util.Xml;
+import android.view.DisplayInfo;
import androidx.test.filters.MediumTest;
@@ -66,10 +71,13 @@
private static final String TASK_TAG = "task";
+ private Rect mParentBounds;
+
@Before
public void setUp() throws Exception {
TaskRecord.setTaskRecordFactory(null);
setupActivityTaskManagerService();
+ mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
}
@Test
@@ -124,6 +132,72 @@
assertTrue(task.returnsToHomeStack());
}
+ /** Ensures that bounds are clipped to their parent. */
+ @Test
+ public void testAppBounds_BoundsClipping() {
+ final Rect shiftedBounds = new Rect(mParentBounds);
+ shiftedBounds.offset(10, 10);
+ final Rect expectedBounds = new Rect(mParentBounds);
+ expectedBounds.intersect(shiftedBounds);
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
+ expectedBounds);
+ }
+
+ /** Ensures that empty bounds are not propagated to the configuration. */
+ @Test
+ public void testAppBounds_EmptyBounds() {
+ final Rect emptyBounds = new Rect();
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
+ null /*ExpectedBounds*/);
+ }
+
+ /** Ensures that bounds on freeform stacks are not clipped. */
+ @Test
+ public void testAppBounds_FreeFormBounds() {
+ final Rect freeFormBounds = new Rect(mParentBounds);
+ freeFormBounds.offset(10, 10);
+ testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
+ freeFormBounds);
+ }
+
+ /** Ensures that fully contained bounds are not clipped. */
+ @Test
+ public void testAppBounds_ContainedBounds() {
+ final Rect insetBounds = new Rect(mParentBounds);
+ insetBounds.inset(5, 5, 5, 5);
+ testStackBoundsConfiguration(
+ WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
+ }
+
+ /** Ensures that full screen free form bounds are clipped */
+ @Test
+ public void testAppBounds_FullScreenFreeFormBounds() {
+ ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+ DisplayInfo info = new DisplayInfo();
+ display.mDisplay.getDisplayInfo(info);
+ final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
+ mParentBounds);
+ }
+
+ private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
+ Rect expectedConfigBounds) {
+
+ ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+ ActivityStack stack = display.createStack(windowingMode, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+
+ final Configuration parentConfig = stack.getConfiguration();
+ parentConfig.windowConfiguration.setAppBounds(parentBounds);
+ task.setBounds(bounds);
+
+ task.resolveOverrideConfiguration(parentConfig);
+ // Assert that both expected and actual are null or are equal to each other
+ assertEquals(expectedConfigBounds,
+ task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
+ }
+
private byte[] serializeToBytes(TaskRecord r) throws IOException, XmlPullParserException {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
final XmlSerializer serializer = Xml.newSerializer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
new file mode 100644
index 0000000..3e32ed3
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Test class for {@link Task}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:TaskTests
+ */
+@SmallTest
+@Presubmit
+public class TaskTests extends WindowTestsBase {
+
+ @Test
+ public void testRemoveContainer() {
+ final StackWindowController stackController1 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final WindowTestUtils.TestAppWindowToken appToken =
+ WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
+
+ task.removeIfPossible();
+ // Assert that the container was removed.
+ assertNull(task.getParent());
+ assertEquals(task.getChildCount(), 0);
+ assertNull(appToken.getParent());
+ }
+
+ @Test
+ public void testRemoveContainer_deferRemoval() {
+ final StackWindowController stackController1 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final WindowTestUtils.TestAppWindowToken appToken =
+ WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
+
+ task.mShouldDeferRemoval = true;
+
+ task.removeIfPossible();
+ // For the case of deferred removal the task will still be connected to the its app token
+ // until the task window container is removed.
+ assertNotNull(task.getParent());
+ assertNotEquals(task.getChildCount(), 0);
+ assertNotNull(appToken.getParent());
+
+ task.removeImmediately();
+ assertNull(task.getParent());
+ assertEquals(task.getChildCount(), 0);
+ assertNull(appToken.getParent());
+ }
+
+ @Test
+ public void testReparent() {
+ final StackWindowController stackController1 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final StackWindowController stackController2 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2);
+
+ boolean gotException = false;
+ try {
+ task.reparent(stackController1, 0, false/* moveParents */);
+ } catch (IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue("Should not be able to reparent to the same parent", gotException);
+
+ final StackWindowController stackController3 =
+ createStackControllerOnDisplay(mDisplayContent);
+ stackController3.setContainer(null);
+ gotException = false;
+ try {
+ task.reparent(stackController3, 0, false/* moveParents */);
+ } catch (IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue("Should not be able to reparent to a stack that doesn't have a container",
+ gotException);
+
+ task.reparent(stackController2, 0, false/* moveParents */);
+ assertEquals(stackController2.mContainer, task.getParent());
+ assertEquals(0, task.positionInParent());
+ assertEquals(1, task2.positionInParent());
+ }
+
+ @Test
+ public void testReparent_BetweenDisplays() {
+ // Create first stack on primary display.
+ final StackWindowController stack1Controller =
+ createStackControllerOnDisplay(mDisplayContent);
+ final TaskStack stack1 = stack1Controller.mContainer;
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1Controller);
+ task.mOnDisplayChangedCalled = false;
+ assertEquals(mDisplayContent, stack1.getDisplayContent());
+
+ // Create second display and put second stack on it.
+ final DisplayContent dc = createNewDisplay();
+ final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
+ final TaskStack stack2 = stack2Controller.mContainer;
+ final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2Controller);
+ // Reparent and check state
+ task.reparent(stack2Controller, 0, false /* moveParents */);
+ assertEquals(stack2, task.getParent());
+ assertEquals(0, task.positionInParent());
+ assertEquals(1, task2.positionInParent());
+ assertTrue(task.mOnDisplayChangedCalled);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
deleted file mode 100644
index bbf508d..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link TaskWindowContainerController}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:TaskWindowContainerControllerTests
- */
-@SmallTest
-@Presubmit
-public class TaskWindowContainerControllerTests extends WindowTestsBase {
-
- /* Comment out due to removal of AppWindowContainerController
- @Test
- public void testRemoveContainer() {
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(this);
- final WindowTestUtils.TestAppWindowContainerController appController =
- new WindowTestUtils.TestAppWindowContainerController(taskController);
-
- taskController.removeContainer();
- // Assert that the container was removed.
- assertNull(taskController.mContainer);
- assertNull(appController.mContainer);
- }
- */
-
- /* Comment out due to removal of AppWindowContainerController
- @Test
- public void testRemoveContainer_deferRemoval() {
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(this);
- final WindowTestUtils.TestAppWindowContainerController appController =
- new WindowTestUtils.TestAppWindowContainerController(taskController);
-
- final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer;
- final AppWindowToken app = appController.mContainer;
- task.mShouldDeferRemoval = true;
-
- taskController.removeContainer();
- // For the case of deferred removal the task controller will no longer be connected to the
- // container, but the app controller will still be connected to the its container until
- // the task window container is removed.
- assertNull(taskController.mContainer);
- assertNull(task.getController());
- assertNotNull(appController.mContainer);
- assertNotNull(app.getController());
-
- task.removeImmediately();
- assertNull(appController.mContainer);
- assertNull(app.getController());
- }
- */
-
- @Test
- public void testReparent() {
- final StackWindowController stackController1 =
- createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stackController1);
- final StackWindowController stackController2 =
- createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController2 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController2);
-
- boolean gotException = false;
- try {
- taskController.reparent(stackController1, 0, false/* moveParents */);
- } catch (IllegalArgumentException e) {
- gotException = true;
- }
- assertTrue("Should not be able to reparent to the same parent", gotException);
-
- final StackWindowController stackController3 =
- createStackControllerOnDisplay(mDisplayContent);
- stackController3.setContainer(null);
- gotException = false;
- try {
- taskController.reparent(stackController3, 0, false/* moveParents */);
- } catch (IllegalArgumentException e) {
- gotException = true;
- }
- assertTrue("Should not be able to reparent to a stack that doesn't have a container",
- gotException);
-
- taskController.reparent(stackController2, 0, false/* moveParents */);
- assertEquals(stackController2.mContainer, taskController.mContainer.getParent());
- assertEquals(0, ((WindowTestUtils.TestTask) taskController.mContainer).positionInParent());
- assertEquals(1, ((WindowTestUtils.TestTask) taskController2.mContainer).positionInParent());
- }
-
- @Test
- public void testReparent_BetweenDisplays() {
- // Create first stack on primary display.
- final StackWindowController stack1Controller =
- createStackControllerOnDisplay(mDisplayContent);
- final TaskStack stack1 = stack1Controller.mContainer;
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stack1Controller);
- final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer;
- task1.mOnDisplayChangedCalled = false;
- assertEquals(mDisplayContent, stack1.getDisplayContent());
-
- // Create second display and put second stack on it.
- final DisplayContent dc = createNewDisplay();
- final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
- final TaskStack stack2 = stack2Controller.mContainer;
- final WindowTestUtils.TestTaskWindowContainerController taskController2 =
- new WindowTestUtils.TestTaskWindowContainerController(stack2Controller);
- final WindowTestUtils.TestTask task2 =
- (WindowTestUtils.TestTask) taskController2.mContainer;
-
- // Reparent and check state
- taskController.reparent(stack2Controller, 0, false /* moveParents */);
- assertEquals(stack2, task1.getParent());
- assertEquals(0, task1.positionInParent());
- assertEquals(1, task2.positionInParent());
- assertTrue(task1.mOnDisplayChangedCalled);
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 612f9ad..8854ede 100644
--- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -49,7 +49,7 @@
mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token);
// Make sure our handler processed the message.
- SystemClock.sleep(100);
+ mWm.mH.runWithScissors(() -> { }, 0);
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
@@ -65,7 +65,7 @@
mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2);
// Make sure our handler processed the message.
- SystemClock.sleep(100);
+ mWm.mH.runWithScissors(() -> { }, 0);
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 3643457..c653762f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -19,7 +19,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOW_CONFIG_ALWAYS_ON_TOP;
import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
import static android.app.WindowConfiguration.WINDOW_CONFIG_ROTATION;
@@ -174,70 +173,4 @@
assertEquals(appBounds.width(), info.appWidth);
assertEquals(appBounds.height(), info.appHeight);
}
-
- /** Ensures that bounds are clipped to their parent. */
- @Test
- public void testAppBounds_BoundsClipping() {
- final Rect shiftedBounds = new Rect(mParentBounds);
- shiftedBounds.offset(10, 10);
- final Rect expectedBounds = new Rect(mParentBounds);
- expectedBounds.intersect(shiftedBounds);
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
- expectedBounds);
- }
-
- /** Ensures that empty bounds are not propagated to the configuration. */
- @Test
- public void testAppBounds_EmptyBounds() {
- final Rect emptyBounds = new Rect();
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
- null /*ExpectedBounds*/);
- }
-
- /** Ensures that bounds on freeform stacks are not clipped. */
- @Test
- public void testAppBounds_FreeFormBounds() {
- final Rect freeFormBounds = new Rect(mParentBounds);
- freeFormBounds.offset(10, 10);
- testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
- freeFormBounds);
- }
-
- /** Ensures that fully contained bounds are not clipped. */
- @Test
- public void testAppBounds_ContainedBounds() {
- final Rect insetBounds = new Rect(mParentBounds);
- insetBounds.inset(5, 5, 5, 5);
- testStackBoundsConfiguration(
- WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
- }
-
- /** Ensures that full screen free form bounds are clipped */
- @Test
- public void testAppBounds_FullScreenFreeFormBounds() {
- final Rect fullScreenBounds = new Rect(0, 0, mDisplayInfo.logicalWidth,
- mDisplayInfo.logicalHeight);
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
- mParentBounds);
- }
-
- private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
- Rect expectedConfigBounds) {
- final StackWindowController stackController = createStackControllerOnStackOnDisplay(
- windowingMode, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-
- final Configuration parentConfig = mDisplayContent.getConfiguration();
- parentConfig.windowConfiguration.setAppBounds(parentBounds);
-
- final Configuration config = new Configuration();
- final WindowConfiguration winConfig = config.windowConfiguration;
- stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/,
- new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
- false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig,
- windowingMode);
- // Assert that both expected and actual are null or are equal to each other
-
- assertEquals(expectedConfigBounds, winConfig.getAppBounds());
- }
-
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
index 7592f1c..de3567e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -93,19 +93,19 @@
controller.setContainer(container);
assertEquals(controller.mContainer, container);
- assertEquals(EMPTY, container.getOverrideConfiguration());
+ assertEquals(EMPTY, container.getRequestedOverrideConfiguration());
final Configuration config = new Configuration();
config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
config.windowConfiguration.setAppBounds(10, 10, 10, 10);
// Assert that the config change through the controller is propagated to the container.
- controller.onOverrideConfigurationChanged(config);
- assertEquals(config, container.getOverrideConfiguration());
+ controller.onRequestedOverrideConfigurationChanged(config);
+ assertEquals(config, container.getRequestedOverrideConfiguration());
// Assert the container configuration isn't changed after removal from the controller.
controller.removeContainer();
- controller.onOverrideConfigurationChanged(EMPTY);
- assertEquals(config, container.getOverrideConfiguration());
+ controller.onRequestedOverrideConfigurationChanged(EMPTY);
+ assertEquals(config, container.getRequestedOverrideConfiguration());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index d4a32cf..60f957f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -424,7 +424,7 @@
final TestWindowContainer parent = grandparent.addChildWindow();
final TestWindowContainer child = parent.addChildWindow();
- child.onOverrideConfigurationChanged(new Configuration());
+ child.onRequestedOverrideConfigurationChanged(new Configuration());
assertTrue(grandparent.mOnDescendantOverrideCalled);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index b3e90de..d9ef10d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -76,7 +76,7 @@
private static class TaskWithBounds extends Task {
final Rect mBounds;
- final Rect mInsetBounds = new Rect();
+ final Rect mOverrideDisplayedBounds = new Rect();
boolean mFullscreenForTest = true;
TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) {
@@ -96,12 +96,12 @@
}
@Override
- public void getOverrideBounds(Rect outBounds) {
+ public void getRequestedOverrideBounds(Rect outBounds) {
outBounds.set(mBounds);
}
@Override
- void getTempInsetBounds(Rect outBounds) {
- outBounds.set(mInsetBounds);
+ Rect getOverrideDisplayedBounds() {
+ return mOverrideDisplayedBounds;
}
@Override
boolean isFullscreen() {
@@ -343,14 +343,14 @@
taskBottom - contentInsetBottom));
pf.set(0, 0, logicalWidth, logicalHeight);
- // However if we set temp inset bounds, the insets will be computed
- // as if our window was laid out there, but it will be laid out according to
- // the task bounds.
+ // If we set displayed bounds, the insets will be computed with the main task bounds
+ // but the frame will be positioned according to the displayed bounds.
final int insetLeft = logicalWidth / 5;
final int insetTop = logicalHeight / 5;
final int insetRight = insetLeft + (taskRight - taskLeft);
final int insetBottom = insetTop + (taskBottom - taskTop);
- task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
+ task.mOverrideDisplayedBounds.set(taskBounds);
+ task.mBounds.set(insetLeft, insetTop, insetRight, insetBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -430,7 +430,6 @@
final int taskBottom = logicalHeight / 4 * 3;
final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
- task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom);
task.mFullscreenForTest = false;
WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
@@ -449,9 +448,9 @@
final int xInset = logicalWidth / 10;
final int yInset = logicalWidth / 10;
final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset);
- Configuration config = new Configuration(w.mAppToken.getOverrideConfiguration());
+ Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration());
config.windowConfiguration.setBounds(cf);
- w.mAppToken.onOverrideConfigurationChanged(config);
+ w.mAppToken.onRequestedOverrideConfigurationChanged(config);
pf.set(0, 0, logicalWidth, logicalHeight);
task.mFullscreenForTest = true;
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
@@ -486,12 +485,12 @@
}
@Test
- public void testDisplayCutout_tempInsetBounds() {
+ public void testDisplayCutout_tempDisplayedBounds() {
// Regular fullscreen task and window
final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm,
- new Rect(0, -500, 1000, 1500));
+ new Rect(0, 0, 1000, 2000));
task.mFullscreenForTest = false;
- task.mInsetBounds.set(0, 0, 1000, 2000);
+ task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 4a99172..522ab9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -30,6 +30,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
import android.app.ActivityManagerInternal;
import android.content.Context;
@@ -125,11 +126,12 @@
if (input != null && input.length > 1) {
doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
}
+ ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+ when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());
mService = WindowManagerService.main(context, ims, false, false,
mPolicy = new TestWindowManagerPolicy(
- WindowManagerServiceRule.this::getWindowManagerService),
- new WindowManagerGlobalLock());
+ WindowManagerServiceRule.this::getWindowManagerService), atms);
mService.mTransactionFactory = () -> {
final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
mSurfaceTransactions.add(new WeakReference<>(transaction));
@@ -147,10 +149,9 @@
mService.onInitReady();
final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
- final DisplayWindowController dcw = new DisplayWindowController(display, mService);
// Display creation is driven by the ActivityManagerService via
// ActivityStackSupervisor. We emulate those steps here.
- mService.mRoot.createDisplayContent(display, dcw);
+ mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
}
private void removeServices() {
@@ -173,21 +174,21 @@
};
}
- public WindowManagerService getWindowManagerService() {
+ WindowManagerService getWindowManagerService() {
return mService;
}
- public TestWindowManagerPolicy getWindowManagerPolicy() {
- return mPolicy;
- }
-
- public void waitUntilWindowManagerHandlersIdle() {
+ void waitUntilWindowManagerHandlersIdle() {
final WindowManagerService wm = getWindowManagerService();
- if (wm != null) {
- wm.mH.runWithScissors(() -> { }, 0);
- wm.mAnimationHandler.runWithScissors(() -> { }, 0);
- SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
+ if (wm == null) {
+ return;
}
+ wm.mH.removeCallbacksAndMessages(null);
+ wm.mAnimationHandler.removeCallbacksAndMessages(null);
+ SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
+ wm.mH.runWithScissors(() -> { }, 0);
+ wm.mAnimationHandler.runWithScissors(() -> { }, 0);
+ SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
}
private void destroyAllSurfaceTransactions() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index a1b0411..65cde77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -19,20 +19,16 @@
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.view.Display;
@@ -42,8 +38,6 @@
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
-import org.mockito.invocation.InvocationOnMock;
-
/**
* A collection of static functions that can be referenced by other test packages to provide access
* to WindowManager related test functionality.
@@ -55,8 +49,8 @@
public static class TestDisplayContent extends DisplayContent {
private TestDisplayContent(Display display, WindowManagerService service,
- DisplayWindowController controller) {
- super(display, service, controller);
+ ActivityDisplay activityDisplay) {
+ super(display, service, activityDisplay);
}
/**
@@ -113,17 +107,6 @@
public static StackWindowController createMockStackWindowContainerController() {
StackWindowController controller = mock(StackWindowController.class);
controller.mContainer = mock(TestTaskStack.class);
-
- // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
- // to properly set bounds values in the configuration. We must mimick those actions here.
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Configuration config = invocationOnMock.<Configuration>getArgument(7);
- final Rect bounds = invocationOnMock.<Rect>getArgument(0);
- config.windowConfiguration.setBounds(bounds);
- return null;
- }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
- anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
-
return controller;
}
@@ -138,6 +121,13 @@
}
}
+ /** Creates an {@link AppWindowToken} and adds it to the specified {@link Task}. */
+ public static TestAppWindowToken createAppWindowTokenInTask(DisplayContent dc, Task task) {
+ final TestAppWindowToken newToken = createTestAppWindowToken(dc);
+ task.addChild(newToken, POSITION_TOP);
+ return newToken;
+ }
+
/**
* An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
* normally be mocked out.
@@ -261,9 +251,10 @@
TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
int resizeMode, boolean supportsPictureInPicture,
- TaskWindowContainerController controller) {
+ TaskRecord taskRecord) {
super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
- new ActivityManager.TaskDescription(), controller);
+ new ActivityManager.TaskDescription(), taskRecord);
+ stack.addTask(this, POSITION_TOP);
}
boolean shouldDeferRemoval() {
@@ -290,49 +281,10 @@
}
}
- /**
- * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
- * class.
- */
- public static class TestTaskWindowContainerController extends TaskWindowContainerController {
-
- static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
- @Override
- public void registerConfigurationChangeListener(
- ConfigurationContainerListener listener) {
- }
-
- @Override
- public void unregisterConfigurationChangeListener(
- ConfigurationContainerListener listener) {
- }
-
- @Override
- public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
- }
-
- @Override
- public void requestResize(Rect bounds, int resizeMode) {
- }
- };
-
- TestTaskWindowContainerController(WindowTestsBase testsBase) {
- this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
- }
-
- TestTaskWindowContainerController(StackWindowController stackController) {
- super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
- RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
- true /* showForAllUsers */, new ActivityManager.TaskDescription(),
- stackController.mService);
- }
-
- @Override
- TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
- boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
- return new TestTask(taskId, stack, userId, mService, resizeMode,
- supportsPictureInPicture, this);
- }
+ public static TestTask createTestTask(StackWindowController stackWindowController) {
+ return new TestTask(sNextTaskId++, stackWindowController.mContainer, 0,
+ stackWindowController.mService, RESIZE_MODE_UNRESIZEABLE, false,
+ mock(TaskRecord.class));
}
public static class TestIApplicationToken implements IApplicationToken {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index b3f56e7..5c3368b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -339,7 +339,7 @@
final int stackId = ++sNextStackId;
final StackWindowController controller = new StackWindowController(stackId, null,
dc.getDisplayId(), true /* onTop */, new Rect(), mWm);
- controller.onOverrideConfigurationChanged(overrideConfig);
+ controller.onRequestedOverrideConfigurationChanged(overrideConfig);
return controller;
}
}
@@ -360,21 +360,17 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
synchronized (mWm.mGlobalLock) {
- return new DisplayContent(display, mWm, mock(DisplayWindowController.class));
+ return new DisplayContent(display, mWm, mock(ActivityDisplay.class));
}
}
/**
* Creates a {@link DisplayContent} with given display state and adds it to the system.
*
- * Unlike {@link #createNewDisplay()} that uses a mock {@link DisplayWindowController} to
- * initialize {@link DisplayContent}, this method used real controller object when the test
- * need to verify its related flows.
- *
* @param displayState For initializing the state of the display. See
* {@link Display#getState()}.
*/
- DisplayContent createNewDisplayWithController(int displayState) {
+ DisplayContent createNewDisplay(int displayState) {
// Leverage main display info & initialize it with display state for given displayId.
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.copyFrom(mDisplayInfo);
@@ -382,11 +378,11 @@
final int displayId = sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- final DisplayWindowController dcw = new DisplayWindowController(display, mWm);
synchronized (mWm.mGlobalLock) {
// Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
// We skip those steps here.
- return mWm.mRoot.createDisplayContent(display, dcw);
+ final ActivityDisplay mockAd = mock(ActivityDisplay.class);
+ return mWm.mRoot.createDisplayContent(display, mockAd);
}
}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4f573a4..65ed85d 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -842,8 +842,8 @@
final boolean previouslyIdle = mAppIdleHistory.isIdle(
event.mPackage, userId, elapsedRealtime);
// Inform listeners if necessary
- if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
- || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+ if ((event.mEventType == UsageEvents.Event.ACTIVITY_RESUMED
+ || event.mEventType == UsageEvents.Event.ACTIVITY_PAUSED
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
|| event.mEventType == UsageEvents.Event.USER_INTERACTION
|| event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -894,8 +894,8 @@
private int usageEventToSubReason(int eventType) {
switch (eventType) {
- case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
- case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+ case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+ case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 8405267..94cc650 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,10 +15,31 @@
*/
package com.android.server.usage;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
+import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
+import static android.app.usage.UsageEvents.Event.KEYGUARD_HIDDEN;
+import static android.app.usage.UsageEvents.Event.KEYGUARD_SHOWN;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
+import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE;
+import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+import static android.app.usage.UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+import static android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION;
+
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
-import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
@@ -125,8 +146,8 @@
/**
* Builds a UsageEvents.Event, but does not add it internally.
*/
- UsageEvents.Event buildEvent(String packageName, String className) {
- UsageEvents.Event event = new UsageEvents.Event();
+ Event buildEvent(String packageName, String className) {
+ Event event = new Event();
event.mPackage = getCachedStringRef(packageName);
if (className != null) {
event.mClass = getCachedStringRef(className);
@@ -138,9 +159,9 @@
* Builds a UsageEvents.Event from a proto, but does not add it internally.
* Built here to take advantage of the cached String Refs
*/
- UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool)
+ Event buildEvent(ProtoInputStream parser, List<String> stringPool)
throws IOException {
- final UsageEvents.Event event = new UsageEvents.Event();
+ final Event event = new Event();
while (true) {
switch (parser.nextField()) {
case (int) IntervalStatsProto.Event.PACKAGE:
@@ -190,20 +211,23 @@
parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX)
- 1));
break;
+ case (int) IntervalStatsProto.Event.INSTANCE_ID:
+ event.mInstanceId = parser.readInt(IntervalStatsProto.Event.INSTANCE_ID);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// Handle default values for certain events types
switch (event.mEventType) {
- case UsageEvents.Event.CONFIGURATION_CHANGE:
+ case CONFIGURATION_CHANGE:
if (event.mConfiguration == null) {
event.mConfiguration = new Configuration();
}
break;
- case UsageEvents.Event.SHORTCUT_INVOCATION:
+ case SHORTCUT_INVOCATION:
if (event.mShortcutId == null) {
event.mShortcutId = "";
}
break;
- case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ case NOTIFICATION_INTERRUPTION:
if (event.mNotificationChannelId == null) {
event.mNotificationChannelId = "";
}
@@ -220,14 +244,15 @@
private boolean isStatefulEvent(int eventType) {
switch (eventType) {
- case UsageEvents.Event.MOVE_TO_FOREGROUND:
- case UsageEvents.Event.MOVE_TO_BACKGROUND:
- case UsageEvents.Event.FOREGROUND_SERVICE_START:
- case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
- case UsageEvents.Event.END_OF_DAY:
- case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
- case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
- case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
+ case ACTIVITY_RESUMED:
+ case ACTIVITY_PAUSED:
+ case ACTIVITY_STOPPED:
+ case FOREGROUND_SERVICE_START:
+ case FOREGROUND_SERVICE_STOP:
+ case END_OF_DAY:
+ case ROLLOVER_FOREGROUND_SERVICE:
+ case CONTINUE_PREVIOUS_DAY:
+ case CONTINUING_FOREGROUND_SERVICE:
return true;
}
return false;
@@ -238,17 +263,56 @@
* interaction. Excludes those that are internally generated.
*/
private boolean isUserVisibleEvent(int eventType) {
- return eventType != UsageEvents.Event.SYSTEM_INTERACTION
- && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+ return eventType != SYSTEM_INTERACTION
+ && eventType != STANDBY_BUCKET_CHANGED;
}
/**
+ * Update the IntervalStats by a activity or foreground service event.
+ * @param packageName package name of this event. Is null if event targets to all packages.
+ * @param className class name of a activity or foreground service, could be null to if this
+ * is sent to all activities/services in this package.
+ * @param timeStamp Epoch timestamp in milliseconds.
+ * @param eventType event type as in {@link Event}
+ * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken.
+ * if className is not an activity, instanceId is not used.
* @hide
*/
@VisibleForTesting
- public void update(String packageName, String className, long timeStamp, int eventType) {
- UsageStats usageStats = getOrCreateUsageStats(packageName);
- usageStats.update(className, timeStamp, eventType);
+ public void update(String packageName, String className, long timeStamp, int eventType,
+ int instanceId) {
+ if (eventType == FLUSH_TO_DISK) {
+ // FLUSH_TO_DISK are sent to all packages.
+ final int size = packageStats.size();
+ for (int i = 0; i < size; i++) {
+ UsageStats usageStats = packageStats.valueAt(i);
+ usageStats.update(null, timeStamp, eventType, instanceId);
+ }
+ } else if (eventType == ACTIVITY_DESTROYED) {
+ UsageStats usageStats = packageStats.get(packageName);
+ if (usageStats != null) {
+ // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED
+ // to ACTIVITY_STOPPED and add to event list.
+ // Otherwise do not add anything to event list. (Because we want to save space
+ // and we do not want a ACTIVITY_STOPPED followed by
+ // ACTIVITY_DESTROYED in event list).
+ final int index = usageStats.mActivities.indexOfKey(instanceId);
+ if (index >= 0) {
+ final int type = usageStats.mActivities.valueAt(index);
+ if (type != ACTIVITY_STOPPED) {
+ Event event = new Event(ACTIVITY_STOPPED, timeStamp);
+ event.mPackage = packageName;
+ event.mClass = className;
+ event.mInstanceId = instanceId;
+ addEvent(event);
+ }
+ }
+ usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId);
+ }
+ } else {
+ UsageStats usageStats = getOrCreateUsageStats(packageName);
+ usageStats.update(className, timeStamp, eventType, instanceId);
+ }
endTime = timeStamp;
}
@@ -256,7 +320,7 @@
* @hide
*/
@VisibleForTesting
- public void addEvent(UsageEvents.Event event) {
+ public void addEvent(Event event) {
if (events == null) {
events = new EventList();
}
@@ -265,7 +329,7 @@
if (event.mClass != null) {
event.mClass = getCachedStringRef(event.mClass);
}
- if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ if (event.mEventType == NOTIFICATION_INTERRUPTION) {
event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId);
}
events.insert(event);
@@ -338,13 +402,13 @@
}
void addEventStatsTo(List<EventStats> out) {
- interactiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE,
+ interactiveTracker.addToEventStats(out, SCREEN_INTERACTIVE,
beginTime, endTime);
- nonInteractiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE,
+ nonInteractiveTracker.addToEventStats(out, SCREEN_NON_INTERACTIVE,
beginTime, endTime);
- keyguardShownTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_SHOWN,
+ keyguardShownTracker.addToEventStats(out, KEYGUARD_SHOWN,
beginTime, endTime);
- keyguardHiddenTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_HIDDEN,
+ keyguardHiddenTracker.addToEventStats(out, KEYGUARD_HIDDEN,
beginTime, endTime);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 8e1c060..d706537 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -135,6 +135,15 @@
stats.mTotalTimeForegroundServiceUsed = proto.readLong(
IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS);
break;
+ case (int) IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS:
+ // Time attributes stored is an offset of the beginTime.
+ stats.mLastTimeVisible = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS);
+ break;
+ case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS:
+ stats.mTotalTimeVisible = proto.readLong(
+ IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS);
+ break;
}
}
if (stats.mLastTimeUsed == 0) {
@@ -337,6 +346,11 @@
usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS,
usageStats.mTotalTimeForegroundServiceUsed);
+ // Time attributes stored as an offset of the beginTime.
+ proto.write(IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS,
+ usageStats.mLastTimeVisible - stats.beginTime);
+ proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
+ usageStats.mTotalTimeVisible);
proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
writeChooserCounts(proto, usageStats);
proto.end(token);
@@ -427,6 +441,7 @@
proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
+ proto.write(IntervalStatsProto.Event.INSTANCE_ID, event.mInstanceId);
switch (event.mEventType) {
case UsageEvents.Event.CONFIGURATION_CHANGE:
if (event.mConfiguration != null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2621252..57dc08f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -16,6 +16,12 @@
package com.android.server.usage;
+import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION;
+import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+
import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -28,10 +34,10 @@
import android.app.usage.EventStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
-import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -75,9 +81,7 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
/**
* A service that collects, aggregates, and persists application usage data.
@@ -106,6 +110,7 @@
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
static final int MSG_UID_STATE_CHANGED = 3;
+ static final int MSG_REPORT_EVENT_TO_ALL_USERID = 4;
private final Object mLock = new Object();
Handler mHandler;
@@ -135,12 +140,10 @@
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
int bucket, int reason) {
- Event event = new Event();
- event.mEventType = Event.STANDBY_BUCKET_CHANGED;
+ Event event = new Event(Event.STANDBY_BUCKET_CHANGED,
+ SystemClock.elapsedRealtime());
event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
event.mPackage = packageName;
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -397,7 +400,7 @@
* Assuming the event's timestamp is measured in milliseconds since boot,
* convert it to a system wall time.
*/
- private void convertToSystemTimeLocked(UsageEvents.Event event) {
+ private void convertToSystemTimeLocked(Event event) {
event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
}
@@ -406,7 +409,6 @@
*/
void shutdown() {
synchronized (mLock) {
- mHandler.removeMessages(MSG_REPORT_EVENT);
flushToDiskLocked();
}
}
@@ -414,7 +416,7 @@
/**
* Called by the Binder stub.
*/
- void reportEvent(UsageEvents.Event event, int userId) {
+ void reportEvent(Event event, int userId) {
synchronized (mLock) {
final long timeNow = checkAndGetTimeLocked();
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -431,14 +433,14 @@
mAppStandby.reportEvent(event, elapsedRealtime, userId);
switch (event.mEventType) {
- case Event.MOVE_TO_FOREGROUND:
+ case Event.ACTIVITY_RESUMED:
try {
mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
} catch (IllegalArgumentException iae) {
Slog.e(TAG, "Failed to note usage start", iae);
}
break;
- case Event.MOVE_TO_BACKGROUND:
+ case Event.ACTIVITY_PAUSED:
try {
mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
} catch (IllegalArgumentException iae) {
@@ -450,10 +452,31 @@
}
/**
- * Called by the Binder stub.
+ * Some events like FLUSH_TO_DISK need to be sent to all userId.
+ * @param event
+ */
+ void reportEventToAllUserId(Event event) {
+ synchronized (mLock) {
+ final int userCount = mUserState.size();
+ for (int i = 0; i < userCount; i++) {
+ Event copy = new Event(event);
+ reportEvent(copy, mUserState.keyAt(i));
+ }
+ }
+ }
+
+ /**
+ * Called by the Handler for message MSG_FLUSH_TO_DISK.
*/
void flushToDisk() {
synchronized (mLock) {
+ // Before flush to disk, report FLUSH_TO_DISK event to signal UsageStats to update app
+ // usage. In case of abrupt power shutdown like battery drain or cold temperature,
+ // all UsageStats has correct data up to last flush to disk.
+ // The FLUSH_TO_DISK event is an internal event, it will not show up in IntervalStats'
+ // EventList.
+ Event event = new Event(FLUSH_TO_DISK, SystemClock.elapsedRealtime());
+ reportEventToAllUserId(event);
flushToDiskLocked();
}
}
@@ -656,9 +679,11 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REPORT_EVENT:
- reportEvent((UsageEvents.Event) msg.obj, msg.arg1);
+ reportEvent((Event) msg.obj, msg.arg1);
break;
-
+ case MSG_REPORT_EVENT_TO_ALL_USERID:
+ reportEventToAllUserId((Event) msg.obj);
+ break;
case MSG_FLUSH_TO_DISK:
flushToDisk();
break;
@@ -1120,20 +1145,11 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(CHOOSER_ACTION, SystemClock.elapsedRealtime());
event.mPackage = packageName;
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = Event.CHOOSER_ACTION;
-
event.mAction = action;
-
event.mContentType = contentType;
-
event.mContentAnnotations = annotations;
-
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1251,37 +1267,29 @@
private final class LocalService extends UsageStatsManagerInternal {
@Override
- public void reportEvent(ComponentName component, int userId, int eventType) {
+ public void reportEvent(ComponentName component, int userId, int eventType,
+ int instanceId) {
if (component == null) {
Slog.w(TAG, "Event reported without a component name");
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(eventType, SystemClock.elapsedRealtime());
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = eventType;
+ event.mInstanceId = instanceId;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
public void reportEvent(String packageName, int userId, int eventType) {
if (packageName == null) {
- Slog.w(TAG, "Event reported without a package name");
+ Slog.w(TAG, "Event reported without a package name, eventType:" + eventType);
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(eventType, SystemClock.elapsedRealtime());
event.mPackage = packageName;
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1292,13 +1300,8 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(CONFIGURATION_CHANGE, SystemClock.elapsedRealtime());
event.mPackage = "android";
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
event.mConfiguration = new Configuration(config);
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1311,14 +1314,9 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(NOTIFICATION_INTERRUPTION, SystemClock.elapsedRealtime());
event.mPackage = packageName.intern();
event.mNotificationChannelId = channelId.intern();
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = Event.NOTIFICATION_INTERRUPTION;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1329,14 +1327,9 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(SHORTCUT_INVOCATION, SystemClock.elapsedRealtime());
event.mPackage = packageName.intern();
event.mShortcutId = shortcutId.intern();
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = Event.SHORTCUT_INVOCATION;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1372,7 +1365,7 @@
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
// we are shutting down.
- shutdown();
+ UsageStatsService.this.shutdown();
}
@Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 01e566c..ec475bf 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -61,11 +61,13 @@
private static final String FLAGS_ATTR = "flags";
private static final String CLASS_ATTR = "class";
private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
+ private static final String TOTAL_TIME_VISIBLE_ATTR = "timeVisible";
private static final String TOTAL_TIME_SERVICE_USED_ATTR = "timeServiceUsed";
private static final String COUNT_ATTR = "count";
private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
private static final String TYPE_ATTR = "type";
+ private static final String INSTANCE_ID_ATTR = "instanceId";
private static final String SHORTCUT_ID_ATTR = "shortcutId";
private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount";
@@ -75,6 +77,7 @@
// Time attributes stored as an offset of the beginTime.
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String LAST_TIME_VISIBLE_ATTR = "lastTimeVisible";
private static final String LAST_TIME_SERVICE_USED_ATTR = "lastTimeServiceUsed";
private static final String END_TIME_ATTR = "endTime";
private static final String TIME_ATTR = "time";
@@ -92,6 +95,13 @@
parser, LAST_TIME_ACTIVE_ATTR);
try {
+ stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_VISIBLE_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mLastTimeVisible", e);
+ }
+
+ try {
stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
@@ -101,8 +111,14 @@
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
try {
+ stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mTotalTimeVisible", e);
+ }
+
+ try {
stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
- TOTAL_TIME_SERVICE_USED_ATTR);
+ TOTAL_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
}
@@ -199,6 +215,13 @@
event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
+
+ try {
+ event.mInstanceId = XmlUtils.readIntAttribute(parser, INSTANCE_ID_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mInstanceId", e);
+ }
+
switch (event.mEventType) {
case UsageEvents.Event.CONFIGURATION_CHANGE:
event.mConfiguration = new Configuration();
@@ -227,10 +250,13 @@
// Write the time offset.
XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
usageStats.mLastTimeUsed - stats.beginTime);
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_VISIBLE_ATTR,
+ usageStats.mLastTimeVisible - stats.beginTime);
XmlUtils.writeLongAttribute(xml, LAST_TIME_SERVICE_USED_ATTR,
usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_VISIBLE_ATTR, usageStats.mTotalTimeVisible);
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_SERVICE_USED_ATTR,
usageStats.mTotalTimeForegroundServiceUsed);
XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
@@ -317,6 +343,7 @@
}
XmlUtils.writeIntAttribute(xml, FLAGS_ATTR, event.mFlags);
XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
+ XmlUtils.writeIntAttribute(xml, INSTANCE_ID_ATTR, event.mInstanceId);
switch (event.mEventType) {
case UsageEvents.Event.CONFIGURATION_CHANGE:
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 94d7dbb..5128ae1 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,10 +16,18 @@
package com.android.server.usage;
+import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
+import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
+import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
+import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY;
+import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY;
+import static android.app.usage.UsageStatsManager.INTERVAL_YEARLY;
+
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
@@ -29,6 +37,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseIntArray;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
@@ -65,7 +74,7 @@
private final int mUserId;
// STOPSHIP: Temporary member variable for debugging b/110930764.
- private UsageEvents.Event mLastEvent;
+ private Event mLastEvent;
private static final long[] INTERVAL_LENGTH = new long[] {
UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
@@ -87,7 +96,7 @@
mContext = context;
mDailyExpiryDate = new UnixCalendar(0);
mDatabase = new UsageStatsDatabase(usageStatsDir);
- mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
+ mCurrentStats = new IntervalStats[INTERVAL_COUNT];
mListener = listener;
mLogPrefix = "User[" + Integer.toString(userId) + "] ";
mUserId = userId;
@@ -125,28 +134,6 @@
updateRolloverDeadline();
}
- // Now close off any events that were open at the time this was saved.
- for (IntervalStats stat : mCurrentStats) {
- final int pkgCount = stat.packageStats.size();
- for (int i = 0; i < pkgCount; i++) {
- final UsageStats pkgStats = stat.packageStats.valueAt(i);
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
- || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
- stat.update(pkgStats.mPackageName, null, stat.lastTimeSaved,
- UsageEvents.Event.END_OF_DAY);
- }
- if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- stat.update(pkgStats.mPackageName, null , stat.lastTimeSaved,
- UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
- }
- notifyStatsChanged();
- }
- }
-
- stat.updateConfigurationStats(null, stat.lastTimeSaved);
- }
-
if (mDatabase.isNewUpdate()) {
notifyNewUpdate();
}
@@ -158,40 +145,46 @@
loadActiveStats(newTime);
}
- void reportEvent(UsageEvents.Event event) {
+ void reportEvent(Event event) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
+ "[" + event.mTimeStamp + "]: "
+ eventToString(event.mEventType));
}
- mLastEvent = new UsageEvents.Event(event);
+ mLastEvent = new Event(event);
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats(event.mTimeStamp);
}
- final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
+ final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
final Configuration newFullConfig = event.mConfiguration;
- if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
- currentDailyStats.activeConfiguration != null) {
+ if (event.mEventType == Event.CONFIGURATION_CHANGE
+ && currentDailyStats.activeConfiguration != null) {
// Make the event configuration a delta.
event.mConfiguration = Configuration.generateDelta(
currentDailyStats.activeConfiguration, newFullConfig);
}
- if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
+ if (event.mEventType != Event.SYSTEM_INTERACTION
+ // ACTIVITY_DESTROYED is a private event. If there is preceding ACTIVITY_STOPPED
+ // ACTIVITY_DESTROYED will be dropped. Otherwise it will be converted to
+ // ACTIVITY_STOPPED.
+ && event.mEventType != Event.ACTIVITY_DESTROYED
+ // FLUSH_TO_DISK is a private event.
+ && event.mEventType != Event.FLUSH_TO_DISK) {
currentDailyStats.addEvent(event);
}
boolean incrementAppLaunch = false;
- if (event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+ if (event.mEventType == Event.ACTIVITY_RESUMED) {
if (event.mPackage != null && !event.mPackage.equals(mLastBackgroundedPackage)) {
incrementAppLaunch = true;
}
- } else if (event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND) {
+ } else if (event.mEventType == Event.ACTIVITY_PAUSED) {
if (event.mPackage != null) {
mLastBackgroundedPackage = event.mPackage;
}
@@ -199,10 +192,10 @@
for (IntervalStats stats : mCurrentStats) {
switch (event.mEventType) {
- case UsageEvents.Event.CONFIGURATION_CHANGE: {
+ case Event.CONFIGURATION_CHANGE: {
stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
} break;
- case UsageEvents.Event.CHOOSER_ACTION: {
+ case Event.CHOOSER_ACTION: {
stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
String[] annotations = event.mContentAnnotations;
if (annotations != null) {
@@ -211,21 +204,21 @@
}
}
} break;
- case UsageEvents.Event.SCREEN_INTERACTIVE: {
+ case Event.SCREEN_INTERACTIVE: {
stats.updateScreenInteractive(event.mTimeStamp);
} break;
- case UsageEvents.Event.SCREEN_NON_INTERACTIVE: {
+ case Event.SCREEN_NON_INTERACTIVE: {
stats.updateScreenNonInteractive(event.mTimeStamp);
} break;
- case UsageEvents.Event.KEYGUARD_SHOWN: {
+ case Event.KEYGUARD_SHOWN: {
stats.updateKeyguardShown(event.mTimeStamp);
} break;
- case UsageEvents.Event.KEYGUARD_HIDDEN: {
+ case Event.KEYGUARD_HIDDEN: {
stats.updateKeyguardHidden(event.mTimeStamp);
} break;
default: {
stats.update(event.mPackage, event.getClassName(),
- event.mTimeStamp, event.mEventType);
+ event.mTimeStamp, event.mEventType, event.mInstanceId);
if (incrementAppLaunch) {
stats.incrementAppLaunchCount(event.mPackage);
}
@@ -286,12 +279,12 @@
*/
private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
StatCombiner<T> combiner) {
- if (intervalType == UsageStatsManager.INTERVAL_BEST) {
+ if (intervalType == INTERVAL_BEST) {
intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
if (intervalType < 0) {
// Nothing saved to disk yet, so every stat is just as equal (no rollover has
// occurred.
- intervalType = UsageStatsManager.INTERVAL_DAILY;
+ intervalType = INTERVAL_DAILY;
}
}
@@ -316,10 +309,10 @@
}
// STOPSHIP: Temporary logging for b/110930764.
- if (intervalType == UsageStatsManager.INTERVAL_DAILY
+ if (intervalType == INTERVAL_DAILY
&& mLastEvent != null && mLastEvent.mTimeStamp >= beginTime) {
final IntervalStats diskStats = mDatabase.getLatestUsageStats(
- UsageStatsManager.INTERVAL_DAILY);
+ INTERVAL_DAILY);
StringBuilder sb = new StringBuilder(256);
sb.append("Last 24 hours of UsageStats missing! timeRange : ");
sb.append(beginTime);
@@ -395,11 +388,11 @@
UsageEvents queryEvents(final long beginTime, final long endTime,
boolean obfuscateInstantApps) {
final ArraySet<String> names = new ArraySet<>();
- List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
- beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+ List<Event> results = queryStats(INTERVAL_DAILY,
+ beginTime, endTime, new StatCombiner<Event>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<UsageEvents.Event> accumulatedResult) {
+ List<Event> accumulatedResult) {
if (stats.events == null) {
return;
}
@@ -411,11 +404,13 @@
return;
}
- UsageEvents.Event event = stats.events.get(i);
+ Event event = stats.events.get(i);
if (obfuscateInstantApps) {
event = event.getObfuscatedIfInstantApp();
}
- names.add(event.mPackage);
+ if (event.mPackage != null) {
+ names.add(event.mPackage);
+ }
if (event.mClass != null) {
names.add(event.mClass);
}
@@ -437,7 +432,7 @@
final String packageName) {
final ArraySet<String> names = new ArraySet<>();
names.add(packageName);
- final List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
+ final List<Event> results = queryStats(INTERVAL_DAILY,
beginTime, endTime, (stats, mutable, accumulatedResult) -> {
if (stats.events == null) {
return;
@@ -450,7 +445,7 @@
return;
}
- final UsageEvents.Event event = stats.events.get(i);
+ final Event event = stats.events.get(i);
if (!packageName.equals(event.mPackage)) {
continue;
}
@@ -492,33 +487,33 @@
// Make a note of which components need a new CONTINUE_PREVIOUS_DAY or
// CONTINUING_FOREGROUND_SERVICE entry.
final Configuration previousConfig =
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
- ArraySet<String> continuePreviousDay = new ArraySet<>();
- ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundActivity =
+ mCurrentStats[INTERVAL_DAILY].activeConfiguration;
+ ArraySet<String> continuePkgs = new ArraySet<>();
+ ArrayMap<String, SparseIntArray> continueActivity =
new ArrayMap<>();
- ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundService =
+ ArrayMap<String, ArrayMap<String, Integer>> continueForegroundService =
new ArrayMap<>();
for (IntervalStats stat : mCurrentStats) {
final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
final UsageStats pkgStats = stat.packageStats.valueAt(i);
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
- || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
- continuePreviousDayForegroundActivity.put(pkgStats.mPackageName,
- pkgStats.mLastForegroundActivityEventMap);
+ if (pkgStats.mActivities.size() > 0
+ || !pkgStats.mForegroundServices.isEmpty()) {
+ if (pkgStats.mActivities.size() > 0) {
+ continueActivity.put(pkgStats.mPackageName,
+ pkgStats.mActivities);
stat.update(pkgStats.mPackageName, null,
mDailyExpiryDate.getTimeInMillis() - 1,
- UsageEvents.Event.END_OF_DAY);
+ Event.END_OF_DAY, 0);
}
- if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- continuePreviousDayForegroundService.put(pkgStats.mPackageName,
- pkgStats.mLastForegroundServiceEventMap);
+ if (!pkgStats.mForegroundServices.isEmpty()) {
+ continueForegroundService.put(pkgStats.mPackageName,
+ pkgStats.mForegroundServices);
stat.update(pkgStats.mPackageName, null,
mDailyExpiryDate.getTimeInMillis() - 1,
- UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
+ Event.ROLLOVER_FOREGROUND_SERVICE, 0);
}
- continuePreviousDay.add(pkgStats.mPackageName);
+ continuePkgs.add(pkgStats.mPackageName);
notifyStatsChanged();
}
}
@@ -532,27 +527,27 @@
mDatabase.prune(currentTimeMillis);
loadActiveStats(currentTimeMillis);
- final int continueCount = continuePreviousDay.size();
+ final int continueCount = continuePkgs.size();
for (int i = 0; i < continueCount; i++) {
- String pkgName = continuePreviousDay.valueAt(i);
- final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
+ String pkgName = continuePkgs.valueAt(i);
+ final long beginTime = mCurrentStats[INTERVAL_DAILY].beginTime;
for (IntervalStats stat : mCurrentStats) {
- if (continuePreviousDayForegroundActivity.containsKey(pkgName)) {
- final ArrayMap<String, Integer> foregroundActivityEventMap =
- continuePreviousDayForegroundActivity.get(pkgName);
- final int size = foregroundActivityEventMap.size();
+ if (continueActivity.containsKey(pkgName)) {
+ final SparseIntArray eventMap =
+ continueActivity.get(pkgName);
+ final int size = eventMap.size();
for (int j = 0; j < size; j++) {
- stat.update(pkgName, foregroundActivityEventMap.keyAt(j), beginTime,
- UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.update(pkgName, null, beginTime,
+ eventMap.valueAt(j), eventMap.keyAt(j));
}
}
- if (continuePreviousDayForegroundService.containsKey(pkgName)) {
- final ArrayMap<String, Integer> foregroundServiceEventMap =
- continuePreviousDayForegroundService.get(pkgName);
- final int size = foregroundServiceEventMap.size();
+ if (continueForegroundService.containsKey(pkgName)) {
+ final ArrayMap<String, Integer> eventMap =
+ continueForegroundService.get(pkgName);
+ final int size = eventMap.size();
for (int j = 0; j < size; j++) {
- stat.update(pkgName, foregroundServiceEventMap.keyAt(j), beginTime,
- UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE);
+ stat.update(pkgName, eventMap.keyAt(j), beginTime,
+ eventMap.valueAt(j), 0);
}
}
stat.updateConfigurationStats(previousConfig, beginTime);
@@ -611,7 +606,7 @@
private void updateRolloverDeadline() {
mDailyExpiryDate.setTimeInMillis(
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
+ mCurrentStats[INTERVAL_DAILY].beginTime);
mDailyExpiryDate.addDays(1);
Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
@@ -660,7 +655,7 @@
}
- void printEvent(IndentingPrintWriter pw, UsageEvents.Event event, boolean prettyDates) {
+ void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
pw.printPair("type", eventToString(event.mEventType));
pw.printPair("package", event.mPackage);
@@ -673,10 +668,15 @@
if (event.mShortcutId != null) {
pw.printPair("shortcutId", event.mShortcutId);
}
- if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
+ if (event.mEventType == Event.STANDBY_BUCKET_CHANGED) {
pw.printPair("standbyBucket", event.getStandbyBucket());
pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
+ } else if (event.mEventType == Event.ACTIVITY_RESUMED
+ || event.mEventType == Event.ACTIVITY_PAUSED
+ || event.mEventType == Event.ACTIVITY_STOPPED) {
+ pw.printPair("instanceId", event.getInstanceId());
}
+
if (event.mNotificationChannelId != null) {
pw.printPair("channelId", event.mNotificationChannelId);
}
@@ -691,11 +691,11 @@
final long beginTime = yesterday.getTimeInMillis();
- List<UsageEvents.Event> events = queryStats(UsageStatsManager.INTERVAL_DAILY,
- beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+ List<Event> events = queryStats(INTERVAL_DAILY,
+ beginTime, endTime, new StatCombiner<Event>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<UsageEvents.Event> accumulatedResult) {
+ List<Event> accumulatedResult) {
if (stats.events == null) {
return;
}
@@ -707,7 +707,7 @@
return;
}
- UsageEvents.Event event = stats.events.get(i);
+ Event event = stats.events.get(i);
if (pkg != null && !pkg.equals(event.mPackage)) {
continue;
}
@@ -727,7 +727,7 @@
pw.println(")");
if (events != null) {
pw.increaseIndent();
- for (UsageEvents.Event event : events) {
+ for (Event event : events) {
printEvent(pw, event, prettyDates);
}
pw.decreaseIndent();
@@ -772,9 +772,17 @@
continue;
}
pw.printPair("package", usageStats.mPackageName);
- pw.printPair("totalTime",
+ pw.printPair("totalTimeUsed",
formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
- pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+ pw.printPair("lastTimeUsed", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+ pw.printPair("totalTimeVisible",
+ formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates));
+ pw.printPair("lastTimeVisible",
+ formatDateTime(usageStats.mLastTimeVisible, prettyDates));
+ pw.printPair("totalTimeFS",
+ formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates));
+ pw.printPair("lastTimeFS",
+ formatDateTime(usageStats.mLastTimeForegroundServiceUsed, prettyDates));
pw.printPair("appLaunchCount", usageStats.mAppLaunchCount);
pw.println();
}
@@ -845,7 +853,7 @@
final EventList events = stats.events;
final int eventCount = events != null ? events.size() : 0;
for (int i = 0; i < eventCount; i++) {
- final UsageEvents.Event event = events.get(i);
+ final Event event = events.get(i);
if (pkg != null && !pkg.equals(event.mPackage)) {
continue;
}
@@ -858,13 +866,13 @@
private static String intervalToString(int interval) {
switch (interval) {
- case UsageStatsManager.INTERVAL_DAILY:
+ case INTERVAL_DAILY:
return "daily";
- case UsageStatsManager.INTERVAL_WEEKLY:
+ case INTERVAL_WEEKLY:
return "weekly";
- case UsageStatsManager.INTERVAL_MONTHLY:
+ case INTERVAL_MONTHLY:
return "monthly";
- case UsageStatsManager.INTERVAL_YEARLY:
+ case INTERVAL_YEARLY:
return "yearly";
default:
return "?";
@@ -873,47 +881,49 @@
private static String eventToString(int eventType) {
switch (eventType) {
- case UsageEvents.Event.NONE:
+ case Event.NONE:
return "NONE";
- case UsageEvents.Event.MOVE_TO_BACKGROUND:
- return "MOVE_TO_BACKGROUND";
- case UsageEvents.Event.MOVE_TO_FOREGROUND:
- return "MOVE_TO_FOREGROUND";
- case UsageEvents.Event.FOREGROUND_SERVICE_START:
+ case Event.ACTIVITY_PAUSED:
+ return "ACTIVITY_PAUSED";
+ case Event.ACTIVITY_RESUMED:
+ return "ACTIVITY_RESUMED";
+ case Event.FOREGROUND_SERVICE_START:
return "FOREGROUND_SERVICE_START";
- case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
+ case Event.FOREGROUND_SERVICE_STOP:
return "FOREGROUND_SERVICE_STOP";
- case UsageEvents.Event.END_OF_DAY:
+ case Event.ACTIVITY_STOPPED:
+ return "ACTIVITY_STOPPED";
+ case Event.END_OF_DAY:
return "END_OF_DAY";
- case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
+ case Event.ROLLOVER_FOREGROUND_SERVICE:
return "ROLLOVER_FOREGROUND_SERVICE";
- case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
+ case Event.CONTINUE_PREVIOUS_DAY:
return "CONTINUE_PREVIOUS_DAY";
- case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
+ case Event.CONTINUING_FOREGROUND_SERVICE:
return "CONTINUING_FOREGROUND_SERVICE";
- case UsageEvents.Event.CONFIGURATION_CHANGE:
+ case Event.CONFIGURATION_CHANGE:
return "CONFIGURATION_CHANGE";
- case UsageEvents.Event.SYSTEM_INTERACTION:
+ case Event.SYSTEM_INTERACTION:
return "SYSTEM_INTERACTION";
- case UsageEvents.Event.USER_INTERACTION:
+ case Event.USER_INTERACTION:
return "USER_INTERACTION";
- case UsageEvents.Event.SHORTCUT_INVOCATION:
+ case Event.SHORTCUT_INVOCATION:
return "SHORTCUT_INVOCATION";
- case UsageEvents.Event.CHOOSER_ACTION:
+ case Event.CHOOSER_ACTION:
return "CHOOSER_ACTION";
- case UsageEvents.Event.NOTIFICATION_SEEN:
+ case Event.NOTIFICATION_SEEN:
return "NOTIFICATION_SEEN";
- case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ case Event.STANDBY_BUCKET_CHANGED:
return "STANDBY_BUCKET_CHANGED";
- case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ case Event.NOTIFICATION_INTERRUPTION:
return "NOTIFICATION_INTERRUPTION";
- case UsageEvents.Event.SLICE_PINNED:
+ case Event.SLICE_PINNED:
return "SLICE_PINNED";
- case UsageEvents.Event.SLICE_PINNED_PRIV:
+ case Event.SLICE_PINNED_PRIV:
return "SLICE_PINNED_PRIV";
- case UsageEvents.Event.SCREEN_INTERACTIVE:
+ case Event.SCREEN_INTERACTIVE:
return "SCREEN_INTERACTIVE";
- case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
+ case Event.SCREEN_NON_INTERACTIVE:
return "SCREEN_NON_INTERACTIVE";
case UsageEvents.Event.KEYGUARD_SHOWN:
return "KEYGUARD_SHOWN";
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 613ba00..904d55e 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -32,7 +32,9 @@
import android.service.usb.UsbHostManagerProto;
import android.service.usb.UsbIsHeadsetProto;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Slog;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -86,6 +88,7 @@
private int mNumConnects; // TOTAL # of connect/disconnect
private final LinkedList<ConnectionRecord> mConnections = new LinkedList<ConnectionRecord>();
private ConnectionRecord mLastConnect;
+ private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
/*
* ConnectionRecord
@@ -300,6 +303,11 @@
if (mode != ConnectionRecord.DISCONNECT) {
mLastConnect = rec;
}
+ if (mode == ConnectionRecord.CONNECT) {
+ mConnected.put(deviceAddress, rec);
+ } else if (mode == ConnectionRecord.DISCONNECT) {
+ mConnected.remove(deviceAddress);
+ }
}
private void logUsbDevice(UsbDescriptorParser descriptorParser) {
@@ -408,6 +416,14 @@
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
parser.getRawDescriptors());
+
+ // Stats collection
+ if (parser.hasAudioInterface()) {
+ StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
+ newDevice.getProductId(), parser.hasAudioInterface(),
+ parser.hasHIDInterface(), parser.hasStorageInterface(),
+ StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
+ }
}
}
@@ -432,9 +448,22 @@
mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
-
+ ConnectionRecord current = mConnected.get(deviceAddress);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
+
+ if (current != null) {
+ UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
+ current.mDescriptors);
+ if (parser.hasAudioInterface()) {
+ // Stats collection
+ StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
+ device.getProductId(), parser.hasAudioInterface(),
+ parser.hasHIDInterface(), parser.hasStorageInterface(),
+ StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
+ System.currentTimeMillis() - current.mTimestamp);
+ }
+ }
} else {
Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
}
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 33da403..96618f5 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -41,12 +41,14 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.usb.UsbPortInfoProto;
import android.service.usb.UsbPortManagerProto;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -54,6 +56,7 @@
import com.android.server.FgThread;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.NoSuchElementException;
/**
@@ -117,6 +120,10 @@
private final ArrayMap<String, RawPortInfo> mSimulatedPorts =
new ArrayMap<>();
+ // Maintains the current connected status of the port.
+ // Uploads logs only when the connection status is changes.
+ private final HashMap<String, Boolean> mConnected = new HashMap<>();
+
public UsbPortManager(Context context) {
mContext = context;
try {
@@ -700,6 +707,18 @@
// Guard against possible reentrance by posting the broadcast from the handler
// instead of from within the critical section.
mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL));
+
+ // Log to statsd
+ if (!mConnected.containsKey(portInfo.mUsbPort.getId())
+ || (mConnected.get(portInfo.mUsbPort.getId())
+ != portInfo.mUsbPortStatus.isConnected())) {
+ mConnected.put(portInfo.mUsbPort.getId(), portInfo.mUsbPortStatus.isConnected());
+ StatsLog.write(StatsLog.USB_CONNECTOR_STATE_CHANGED,
+ portInfo.mUsbPortStatus.isConnected()
+ ? StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_CONNECTED :
+ StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
+ portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis);
+ }
}
private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
@@ -746,7 +765,12 @@
public boolean mCanChangeMode;
public boolean mCanChangePowerRole;
public boolean mCanChangeDataRole;
- public int mDisposition; // default initialized to 0 which means added
+ // default initialized to 0 which means added
+ public int mDisposition;
+ // Tracks elapsedRealtime() of when the port was connected
+ public long mConnectedAtMillis;
+ // 0 when port is connected. Else reports the last connected duration
+ public long mLastConnectDurationMillis;
public PortInfo(String portId, int supportedModes) {
mUsbPort = new UsbPort(portId, supportedModes);
@@ -756,6 +780,8 @@
int currentPowerRole, boolean canChangePowerRole,
int currentDataRole, boolean canChangeDataRole,
int supportedRoleCombinations) {
+ boolean dispositionChanged = false;
+
mCanChangeMode = canChangeMode;
mCanChangePowerRole = canChangePowerRole;
mCanChangeDataRole = canChangeDataRole;
@@ -767,9 +793,18 @@
!= supportedRoleCombinations) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations);
- return true;
+ dispositionChanged = true;
}
- return false;
+
+ if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) {
+ mConnectedAtMillis = SystemClock.elapsedRealtime();
+ mLastConnectDurationMillis = 0;
+ } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) {
+ mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis;
+ mConnectedAtMillis = 0;
+ }
+
+ return dispositionChanged;
}
void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
@@ -782,6 +817,10 @@
mCanChangePowerRole);
dump.write("can_change_data_role", UsbPortInfoProto.CAN_CHANGE_DATA_ROLE,
mCanChangeDataRole);
+ dump.write("connected_at_millis",
+ UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis);
+ dump.write("last_connect_duration_millis",
+ UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis);
dump.end(token);
}
@@ -791,7 +830,9 @@
return "port=" + mUsbPort + ", status=" + mUsbPortStatus
+ ", canChangeMode=" + mCanChangeMode
+ ", canChangePowerRole=" + mCanChangePowerRole
- + ", canChangeDataRole=" + mCanChangeDataRole;
+ + ", canChangeDataRole=" + mCanChangeDataRole
+ + ", connectedAtMillis=" + mConnectedAtMillis
+ + ", lastConnectDurationMillis=" + mLastConnectDurationMillis;
}
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index cef99865..36d0188 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -29,7 +29,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
-import java.lang.String;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
@@ -123,10 +122,21 @@
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
+ * @deprecated Use the list from {@link #EXTRA_SUGGESTED_PHONE_ACCOUNTS} instead.
*/
+ @Deprecated
public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
/**
+ * Key for extra used to pass along a list of {@link PhoneAccountSuggestion}s to the in-call
+ * UI when a call enters the {@link #STATE_SELECT_PHONE_ACCOUNT} state. The list included here
+ * will have the same length and be in the same order as the list passed with
+ * {@link #AVAILABLE_PHONE_ACCOUNTS}.
+ */
+ public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS =
+ "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
+
+ /**
* Extra key used to indicate the time (in milliseconds since midnight, January 1, 1970 UTC)
* when the last outgoing emergency call was made. This is used to identify potential emergency
* callbacks.
@@ -908,10 +918,16 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("[pa: ");
+ sb.append("[id: ");
+ sb.append(mTelecomCallId);
+ sb.append(", pa: ");
sb.append(mAccountHandle);
sb.append(", hdl: ");
- sb.append(Log.pii(mHandle));
+ sb.append(Log.piiHandle(mHandle));
+ sb.append(", hdlPres: ");
+ sb.append(mHandlePresentation);
+ sb.append(", videoState: ");
+ sb.append(VideoProfile.videoStateToString(mVideoState));
sb.append(", caps: ");
sb.append(capabilitiesToString(mCallCapabilities));
sb.append(", props: ");
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7db6940..6628743 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -27,8 +27,8 @@
import android.os.RemoteException;
import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallScreeningService;
import com.android.internal.telecom.ICallScreeningAdapter;
+import com.android.internal.telecom.ICallScreeningService;
/**
* This service can be implemented by the default dialer (see
@@ -147,7 +147,7 @@
private boolean mShouldSkipCallLog;
private boolean mShouldSkipNotification;
- /*
+ /**
* Sets whether the incoming call should be blocked.
*/
public Builder setDisallowCall(boolean shouldDisallowCall) {
@@ -155,7 +155,7 @@
return this;
}
- /*
+ /**
* Sets whether the incoming call should be disconnected as if the user had manually
* rejected it. This property should only be set to true if the call is disallowed.
*/
@@ -164,16 +164,20 @@
return this;
}
- /*
+ /**
* Sets whether the incoming call should not be displayed in the call log. This property
* should only be set to true if the call is disallowed.
+ * <p>
+ * Note: Calls will still be logged with type
+ * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
+ * is set.
*/
public Builder setSkipCallLog(boolean shouldSkipCallLog) {
mShouldSkipCallLog = shouldSkipCallLog;
return this;
}
- /*
+ /**
* Sets whether a missed call notification should not be shown for the incoming call.
* This property should only be set to true if the call is disallowed.
*/
@@ -211,6 +215,17 @@
* Called when a new incoming call is added.
* {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
* should be called to allow or disallow the call.
+ * <p>
+ * Note: The {@link Call.Details} instance provided to a call screening service will only have
+ * the following properties set. The rest of the {@link Call.Details} properties will be set to
+ * their default value or {@code null}.
+ * <ul>
+ * <li>{@link Call.Details#getState()}</li>
+ * <li>{@link Call.Details#getConnectTimeMillis()}</li>
+ * <li>{@link Call.Details#getCreationTimeMillis()}</li>
+ * <li>{@link Call.Details#getHandle()}</li>
+ * <li>{@link Call.Details#getHandlePresentation()}</li>
+ * </ul>
*
* @param callDetails Information about a new incoming call, see {@link Call.Details}.
*/
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
new file mode 100644
index 0000000..4e6a178
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public final class PhoneAccountSuggestion implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {REASON_NONE, REASON_INTRA_CARRIER, REASON_FREQUENT,
+ REASON_USER_SET, REASON_OTHER}, prefix = { "REASON_" })
+ public @interface SuggestionReason {}
+
+ /**
+ * Indicates that this account is not suggested for use, but is still available.
+ */
+ public static final int REASON_NONE = 0;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested because the number we're calling
+ * is on the same carrier, and therefore may have lower rates.
+ */
+ public static final int REASON_INTRA_CARRIER = 1;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested because the user uses it
+ * frequently for the number that we are calling.
+ */
+ public static final int REASON_FREQUENT = 2;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested because the user explicitly
+ * specified that it be used for the number we are calling.
+ */
+ public static final int REASON_USER_SET = 3;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested for a reason not otherwise
+ * enumerated here.
+ */
+ public static final int REASON_OTHER = 4;
+
+ private PhoneAccountHandle mHandle;
+ private int mReason;
+ private boolean mShouldAutoSelect;
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public PhoneAccountSuggestion(PhoneAccountHandle handle, @SuggestionReason int reason,
+ boolean shouldAutoSelect) {
+ this.mHandle = handle;
+ this.mReason = reason;
+ this.mShouldAutoSelect = shouldAutoSelect;
+ }
+
+ private PhoneAccountSuggestion(Parcel in) {
+ mHandle = in.readParcelable(PhoneAccountHandle.class.getClassLoader());
+ mReason = in.readInt();
+ mShouldAutoSelect = in.readByte() != 0;
+ }
+
+ public static final Creator<PhoneAccountSuggestion> CREATOR =
+ new Creator<PhoneAccountSuggestion>() {
+ @Override
+ public PhoneAccountSuggestion createFromParcel(Parcel in) {
+ return new PhoneAccountSuggestion(in);
+ }
+
+ @Override
+ public PhoneAccountSuggestion[] newArray(int size) {
+ return new PhoneAccountSuggestion[size];
+ }
+ };
+
+ /**
+ * @return The {@link PhoneAccountHandle} for this suggestion.
+ */
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return mHandle;
+ }
+
+ /**
+ * @return The reason for this suggestion
+ */
+ public @SuggestionReason int getReason() {
+ return mReason;
+ }
+
+ /**
+ * Suggests whether the dialer should automatically place the call using this account without
+ * user interaction. This may be set on multiple {@link PhoneAccountSuggestion}s, and the dialer
+ * is free to choose which one to use.
+ * @return {@code true} if the hint is to auto-select, {@code false} otherwise.
+ */
+ public boolean shouldAutoSelect() {
+ return mShouldAutoSelect;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mHandle, flags);
+ dest.writeInt(mReason);
+ dest.writeByte((byte) (mShouldAutoSelect ? 1 : 0));
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9f0bdd7..49030f5 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -460,6 +460,12 @@
"android.telecom.extra.START_CALL_WITH_RTT";
/**
+ * A boolean extra set to indicate whether an app is eligible to be bound to when there are
+ * ongoing calls on the device.
+ */
+ public static final String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
+
+ /**
* A boolean meta-data value indicating whether an {@link InCallService} implements an
* in-call user interface. Dialer implementations (see {@link #getDefaultDialerPackage()}) which
* would also like to replace the in-call interface should set this meta-data to {@code true} in
@@ -471,8 +477,6 @@
* A boolean meta-data value indicating whether an {@link InCallService} implements an
* in-call user interface to be used while the device is in car-mode (see
* {@link android.content.res.Configuration.UI_MODE_TYPE_CAR}).
- *
- * @hide
*/
public static final String METADATA_IN_CALL_SERVICE_CAR_MODE_UI =
"android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
@@ -1462,7 +1466,6 @@
* otherwise.
*/
@RequiresPermission(Manifest.permission.ANSWER_PHONE_CALLS)
- @SystemApi
public boolean endCall() {
try {
if (isServiceConnected()) {
@@ -1539,7 +1542,6 @@
/**
* Returns whether TTY is supported on this device.
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
@@ -2040,7 +2042,6 @@
} catch (RemoteException e) {
Log.e(TAG, "RemoteException handleCallIntent: " + e);
}
-
}
private ITelecomService getTelecomService() {
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 143b323..d64efea 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -305,6 +305,8 @@
*/
void handleCallIntent(in Intent intent);
+ void setTestDefaultCallRedirectionApp(String packageName);
+
void setTestDefaultCallScreeningApp(String packageName);
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 054288b..2236cba 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -13,3 +13,4 @@
shuoq@google.com
refuhoo@google.com
paulye@google.com
+nazaninb@google.com
\ No newline at end of file
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index b3e1ffa..1cbe5a2 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -44,6 +45,8 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsApplication;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
@@ -2962,34 +2965,36 @@
*@hide
*/
@SystemApi
- public static final String MODEM_COGNITIVE = "modem_cognitive";
+ public static final String MODEM_PERSIST = "modem_cognitive";
/**
- * The max connections of this APN.
+ * The max number of connections of this APN.
* <p>Type: INTEGER</p>
*@hide
*/
@SystemApi
- public static final String MAX_CONNS = "max_conns";
+ public static final String MAX_CONNECTIONS = "max_conns";
/**
- * The wait time for retry of the APN.
+ * The wait time for retrying the APN, in milliseconds.
* <p>Type: INTEGER</p>
*@hide
*/
@SystemApi
- public static final String WAIT_TIME = "wait_time";
+ public static final String WAIT_TIME_RETRY = "wait_time";
/**
- * The time to limit max connection for the APN.
+ * The max number of seconds this APN will support its maximum number of connections
+ * as defined in {@link #MAX_CONNECTIONS}.
* <p>Type: INTEGER</p>
*@hide
*/
@SystemApi
- public static final String MAX_CONNS_TIME = "max_conns_time";
+ public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
/**
- * The MTU(Maxinum transmit unit) size of the mobile interface to which the APN connected.
+ * The MTU (maximum transmit unit) size of the mobile interface to which the APN is
+ * connected, in bytes.
* <p>Type: INTEGER </p>
* @hide
*/
@@ -2998,11 +3003,19 @@
/**
* APN edit status. APN could be added/edited/deleted by a user or carrier.
+ * see all possible returned APN edit status.
+ * <ul>
+ * <li>{@link #UNEDITED}</li>
+ * <li>{@link #USER_EDITED}</li>
+ * <li>{@link #USER_DELETED}</li>
+ * <li>{@link #CARRIER_EDITED}</li>
+ * <li>{@link #CARRIER_DELETED}</li>
+ * </ul>
* <p>Type: INTEGER </p>
* @hide
*/
@SystemApi
- public static final String EDITED = "edited";
+ public static final String EDITED_STATUS = "edited";
/**
* {@code true} if this APN visible to the user, {@code false} otherwise.
@@ -3021,59 +3034,61 @@
public static final String USER_EDITABLE = "user_editable";
/**
- * {@link #EDITED APN edit status} indicates that this APN has not been edited or fails to
- * edit.
+ * {@link #EDITED_STATUS APN edit status} indicates that this APN has not been edited or
+ * fails to edit.
* <p>Type: INTEGER </p>
* @hide
*/
@SystemApi
- public static final int UNEDITED = 0;
+ public static final @EditStatus int UNEDITED = 0;
/**
- * {@link #EDITED APN edit status} indicates that this APN has been edited by users.
+ * {@link #EDITED_STATUS APN edit status} indicates that this APN has been edited by users.
* <p>Type: INTEGER </p>
* @hide
*/
@SystemApi
- public static final int USER_EDITED = 1;
+ public static final @EditStatus int USER_EDITED = 1;
/**
- * {@link #EDITED APN edit status} indicates that this APN has been deleted by users.
+ * {@link #EDITED_STATUS APN edit status} indicates that this APN has been deleted by users.
* <p>Type: INTEGER </p>
* @hide
*/
@SystemApi
- public static final int USER_DELETED = 2;
+ public static final @EditStatus int USER_DELETED = 2;
/**
- * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
- * deleted by the user is still present in the new APN database and therefore must remain
- * tagged as user deleted rather than completely removed from the database.
+ * {@link #EDITED_STATUS APN edit status} is an intermediate value used to indicate that an
+ * entry deleted by the user is still present in the new APN database and therefore must
+ * remain tagged as user deleted rather than completely removed from the database.
* @hide
*/
public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
/**
- * {@link #EDITED APN edit status} indicates that this APN has been edited by carriers.
+ * {@link #EDITED_STATUS APN edit status} indicates that this APN has been edited by
+ * carriers.
* <p>Type: INTEGER </p>
* @hide
*/
@SystemApi
- public static final int CARRIER_EDITED = 4;
+ public static final @EditStatus int CARRIER_EDITED = 4;
/**
- * {@link #EDITED APN edit status} indicates that this APN has been deleted by carriers.
- * CARRIER_DELETED values are currently not used as there is no use case. If they are used,
- * delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+ * {@link #EDITED_STATUS APN edit status} indicates that this APN has been deleted by
+ * carriers. CARRIER_DELETED values are currently not used as there is no use case.
+ * If they are used, delete() will have to change accordingly. Currently it is hardcoded to
+ * USER_DELETED.
* <p>Type: INTEGER </p>
* @hide
*/
- public static final int CARRIER_DELETED = 5;
+ public static final @EditStatus int CARRIER_DELETED = 5;
/**
- * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
- * deleted by the carrier is still present in the new APN database and therefore must remain
- * tagged as user deleted rather than completely removed from the database.
+ * {@link #EDITED_STATUS APN edit status} is an intermediate value used to indicate that an
+ * entry deleted by the carrier is still present in the new APN database and therefore must
+ * remain tagged as user deleted rather than completely removed from the database.
* @hide
*/
public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
@@ -3110,14 +3125,14 @@
public static final String APN_SET_ID = "apn_set_id";
/**
- * Possible value for the{@link #APN_SET_ID} field. By default APNs will not belong to a
- * set. If the user manually selects an APN with no set set, there is no need to prioritize
- * any specific APN set ids.
+ * Possible value for the {@link #APN_SET_ID} field. By default APNs will not belong to a
+ * set. If the user manually selects an APN without apn set id, there is no need to
+ * prioritize any specific APN set ids.
* <p>Type: INTEGER</p>
* @hide
*/
@SystemApi
- public static final int NO_SET_SET = 0;
+ public static final int NO_APN_SET_ID = 0;
/**
* A unique carrier id associated with this APN
@@ -3126,6 +3141,17 @@
*/
public static final String CARRIER_ID = "carrier_id";
+ /** @hide */
+ @IntDef({
+ UNEDITED,
+ USER_EDITED,
+ USER_DELETED,
+ CARRIER_DELETED,
+ CARRIER_EDITED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EditStatus {}
+
}
/**
@@ -3614,8 +3640,9 @@
/**
* Generates a content {@link Uri} used to receive updates on precise carrier identity
- * change on the given subscriptionId
- * {@link TelephonyManager#ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED}.
+ * change on the given subscriptionId returned by
+ * {@link TelephonyManager#getSimPreciseCarrierId()}.
+ * @see TelephonyManager#ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED
* <p>
* Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
* precise carrier identity {@link TelephonyManager#getSimPreciseCarrierId()}
@@ -3626,7 +3653,6 @@
*
* @param subscriptionId the subscriptionId to receive updates on
* @return the Uri used to observe precise carrier identity changes
- * @hide
*/
public static Uri getPreciseCarrierIdUriForSubscriptionId(int subscriptionId) {
return Uri.withAppendedPath(Uri.withAppendedPath(CONTENT_URI, "precise"),
@@ -3648,24 +3674,22 @@
public static final String CARRIER_ID = "carrier_id";
/**
- * A user facing carrier name for precise carrier id.
- * @see TelephonyManager#getSimPreciseCarrierIdName()
- * This is not a database column, only used to notify content observers for
- * {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
- * @hide
- */
- public static final String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
-
- /**
* A fine-grained carrier id.
* @see TelephonyManager#getSimPreciseCarrierId()
* This is not a database column, only used to notify content observers for
* {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
- * @hide
*/
public static final String PRECISE_CARRIER_ID = "precise_carrier_id";
/**
+ * A user facing carrier name for precise carrier id {@link #PRECISE_CARRIER_ID}.
+ * @see TelephonyManager#getSimPreciseCarrierIdName()
+ * This is not a database column, only used to notify content observers for
+ * {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
+ */
+ public static final String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
+
+ /**
* A unique parent carrier id. The parent-child
* relationship can be used to further differentiate a single carrier by different networks,
* by prepaid v.s. postpaid or even by 4G v.s. 3G plan. It's an optional field.
@@ -3677,18 +3701,6 @@
public static final String PARENT_CARRIER_ID = "parent_carrier_id";
/**
- * A unique mno carrier id. mno carrier shares the same {@link All#MCCMNC} as carrier id
- * and can be solely identified by {@link All#MCCMNC} only. If there is no such mno
- * carrier, then mno carrier id equals to {@link #CARRIER_ID carrier id}.
- *
- * <p>mno carrier id can be used as fallback id. When the exact carrier id configurations
- * are not found, usually fall back to its mno carrier id.
- * <P>Type: INTEGER </P>
- * @hide
- */
- public static final String MNO_CARRIER_ID = "mno_carrier_id";
-
- /**
* Contains mappings between matching rules with carrier id for all carriers.
* @hide
*/
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/telephony/java/android/telephony/AvailableNetworkInfo.aidl
similarity index 89%
copy from core/java/android/service/intelligence/SnapshotData.aidl
copy to telephony/java/android/telephony/AvailableNetworkInfo.aidl
index 31d1339..1d4378c 100644
--- a/core/java/android/service/intelligence/SnapshotData.aidl
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.aidl
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.telephony;
-parcelable SnapshotData;
+parcelable AvailableNetworkInfo;
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
new file mode 100644
index 0000000..fe07370
--- /dev/null
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Defines available network information which includes corresponding subscription id,
+ * network plmns and corresponding priority to be used for network selection by Alternative Network
+ * Service.
+ */
+public final class AvailableNetworkInfo implements Parcelable {
+
+ /*
+ * Defines number of priority level high.
+ */
+ public static final int PRIORITY_HIGH = 1;
+
+ /*
+ * Defines number of priority level medium.
+ */
+ public static final int PRIORITY_MED = 2;
+
+ /*
+ * Defines number of priority level low.
+ */
+ public static final int PRIORITY_LOW = 3;
+
+ /**
+ * subscription Id of the available network. This value must be one of the entry retrieved from
+ * {@link SubscriptionManager#getOpportunisticSubscriptions}
+ */
+ private int mSubId;
+
+ /**
+ * Priority for the subscription id.
+ * Priorities are in the range of 1 to 3 where 1
+ * has the highest priority.
+ */
+ private int mPriority;
+
+ /**
+ * Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
+ * If this entry is left empty, then the platform software will not scan the network
+ * to revalidate the input.
+ */
+ private ArrayList<String> mMccMncs;
+
+ /**
+ * Return subscription Id of the available network.
+ * This value must be one of the entry retrieved from
+ * {@link SubscriptionManager#getOpportunisticSubscriptions}
+ * @return subscription id
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ /**
+ * Return priority for the subscription id. Valid value will be within
+ * [{@link AvailableNetworkInfo#PRIORITY_HIGH}, {@link AvailableNetworkInfo#PRIORITY_LOW}]
+ * @return priority level
+ */
+ public int getPriority() {
+ return mPriority;
+ }
+
+ /**
+ * Return List of PLMN ids (MCC-MNC) associated with the sub ID.
+ * If this entry is left empty, then the platform software will not scan the network
+ * to revalidate the input.
+ * @return list of PLMN ids
+ */
+ public List<String> getMccMncs() {
+ return (List<String>) mMccMncs.clone();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSubId);
+ dest.writeInt(mPriority);
+ dest.writeStringList(mMccMncs);
+ }
+
+ private AvailableNetworkInfo(Parcel in) {
+ mSubId = in.readInt();
+ mPriority = in.readInt();
+ in.readStringList(mMccMncs);
+ }
+
+ public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) {
+ mSubId = subId;
+ mPriority = priority;
+ mMccMncs = new ArrayList<String>(mccMncs);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ AvailableNetworkInfo ani;
+
+ try {
+ ani = (AvailableNetworkInfo) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (mSubId == ani.mSubId
+ && mPriority == ani.mPriority
+ && (((mMccMncs != null)
+ && mMccMncs.equals(ani.mMccMncs))));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSubId, mPriority, mMccMncs);
+ }
+
+ public static final Parcelable.Creator<AvailableNetworkInfo> CREATOR =
+ new Creator<AvailableNetworkInfo>() {
+ @Override
+ public AvailableNetworkInfo createFromParcel(Parcel in) {
+ return new AvailableNetworkInfo(in);
+ }
+
+ @Override
+ public AvailableNetworkInfo[] newArray(int size) {
+ return new AvailableNetworkInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return ("AvailableNetworkInfo:"
+ + " mSubId: " + mSubId
+ + " mPriority: " + mPriority
+ + " mMccMncs: " + Arrays.toString(mMccMncs.toArray()));
+ }
+}
+
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 185c886..cfe134fd 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -68,7 +68,13 @@
* This intent is broadcast by the system when carrier config changes. An int is specified in
* {@link #EXTRA_SLOT_INDEX} to indicate the slot index that this is for. An optional int extra
* {@link #EXTRA_SUBSCRIPTION_INDEX} is included to indicate the subscription index if a valid
- * one is available for the slot index.
+ * one is available for the slot index. An optional int extra
+ * {@link TelephonyManager#EXTRA_CARRIER_ID} is included to indicate the carrier id for the
+ * changed carrier configuration. An optional int extra
+ * {@link TelephonyManager#EXTRA_PRECISE_CARRIER_ID} is included to indicate the precise
+ * carrier id for the changed carrier configuration.
+ * @see TelephonyManager#getSimCarrierId()
+ * @see TelephonyManager#getSimPreciseCarrierId()
*/
public static final String
ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
@@ -78,6 +84,14 @@
// system image, that can be added in packages/apps/CarrierConfig.
/**
+ * Specifies a value that identifies the version of the carrier configuration that is
+ * currently in use. This string is displayed on the UI.
+ * The format of the string is not specified.
+ */
+ public static final String KEY_CARRIER_CONFIG_VERSION_STRING =
+ "carrier_config_version_string";
+
+ /**
* This flag specifies whether VoLTE availability is based on provisioning. By default this is
* false.
*/
@@ -394,7 +408,6 @@
* @see SubscriptionManager#getSubscriptionPlans(int)
* @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
*/
- @SystemApi
public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING =
"config_plans_package_override_string";
@@ -1154,20 +1167,33 @@
*/
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
- /**
- * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
- * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
- * the carrier
- * CallScreeningService with the opportunity to allow or block calls.
- * <p>
- * The String includes the package name/the class name.
- * Example:
- * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
- * <p>
- * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
- * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
- * ComponentName.
- */
+ /**
+ * String to override sim country iso.
+ * Sim country iso is based on sim MCC which is coarse and doesn't work with dual IMSI SIM where
+ * a SIM can have multiple MCC from different countries.
+ * Instead, each sim carrier should have a single country code, apply per carrier based iso
+ * code as an override. The overridden value can be read from
+ * {@link TelephonyManager#getSimCountryIso()} and {@link SubscriptionInfo#getCountryIso()}
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING =
+ "sim_country_iso_override_string";
+
+ /**
+ * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
+ * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
+ * the carrier
+ * CallScreeningService with the opportunity to allow or block calls.
+ * <p>
+ * The String includes the package name/the class name.
+ * Example:
+ * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
+ * <p>
+ * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
+ * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
+ * ComponentName.
+ */
public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
/**
@@ -2348,11 +2374,38 @@
public static final String KEY_CALL_WAITING_SERVICE_CLASS_INT =
"call_waiting_service_class_int";
+ /**
+ * This configuration allow the system UI to display different 5G icon for different 5G status.
+ *
+ * There are four 5G status:
+ * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
+ * millimeter wave.
+ * 2. connected: device currently connected to 5G cell as the secondary cell but not using
+ * millimeter wave.
+ * 3. not_restricted: device camped on a network that has 5G capability(not necessary to connect
+ * a 5G cell as a secondary cell) and the use of 5G is not restricted.
+ * 4. restricted: device camped on a network that has 5G capability(not necessary to connect a
+ * 5G cell as a secondary cell) but the use of 5G is restricted.
+ *
+ * The configured string contains multiple key-value pairs separated by comma. For each pair,
+ * the key and value is separated by a colon. The key is corresponded to a 5G status above and
+ * the value is the icon name. Use "None" as the icon name if no icon should be shown in a
+ * specific 5G status.
+ *
+ * Here is an example of the configuration:
+ * "connected_mmwave:5GPlus,connected:5G,not_restricted:None,restricted:None"
+ *
+ * @hide
+ */
+ public static final String KEY_5G_ICON_CONFIGURATION_STRING =
+ "5g_icon_configuration_string";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
static {
sDefaults = new PersistableBundle();
+ sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
@@ -2533,6 +2586,7 @@
sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+ sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
@@ -2708,6 +2762,8 @@
sDefaults.putBoolean(KEY_USE_CALL_FORWARDING_USSD_BOOL, false);
sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
+ sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
+ "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
}
/**
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
index b6e6cba..5d809d0 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationStates.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -33,17 +33,31 @@
*/
public final boolean isNrAvailable;
+ /**
+ * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving
+ * cell.
+ *
+ * True the primary serving cell is LTE cell and the plmn-InfoList-r15 is present in SIB2 and
+ * at least one bit in this list is true, otherwise this value should be false.
+ *
+ * Reference: 3GPP TS 36.331 v15.2.2 6.3.1 System information blocks.
+ */
+ public final boolean isEnDcAvailable;
+
DataSpecificRegistrationStates(
- int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable) {
+ int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
+ boolean isEnDcAvailable) {
this.maxDataCalls = maxDataCalls;
this.isDcNrRestricted = isDcNrRestricted;
this.isNrAvailable = isNrAvailable;
+ this.isEnDcAvailable = isEnDcAvailable;
}
private DataSpecificRegistrationStates(Parcel source) {
maxDataCalls = source.readInt();
isDcNrRestricted = source.readBoolean();
isNrAvailable = source.readBoolean();
+ isEnDcAvailable = source.readBoolean();
}
@Override
@@ -51,6 +65,7 @@
dest.writeInt(maxDataCalls);
dest.writeBoolean(isDcNrRestricted);
dest.writeBoolean(isNrAvailable);
+ dest.writeBoolean(isEnDcAvailable);
}
@Override
@@ -65,13 +80,14 @@
.append(" maxDataCalls = " + maxDataCalls)
.append(" isDcNrRestricted = " + isDcNrRestricted)
.append(" isNrAvailable = " + isNrAvailable)
+ .append(" isEnDcAvailable = " + isEnDcAvailable)
.append(" }")
.toString();
}
@Override
public int hashCode() {
- return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable);
+ return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable);
}
@Override
@@ -83,7 +99,8 @@
DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
return this.maxDataCalls == other.maxDataCalls
&& this.isDcNrRestricted == other.isDcNrRestricted
- && this.isNrAvailable == other.isNrAvailable;
+ && this.isNrAvailable == other.isNrAvailable
+ && this.isEnDcAvailable == other.isEnDcAvailable;
}
public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index aee744f..b00665e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -219,12 +219,13 @@
public NetworkRegistrationState(int domain, int transportType, int regState,
int accessNetworkTechnology, int rejectCause, boolean emergencyOnly,
int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls,
- boolean isDcNrRestricted, boolean isNrAvailable) {
+ boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable) {
this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
availableServices, cellIdentity);
mDataSpecificStates = new DataSpecificRegistrationStates(
- maxDataCalls, isDcNrRestricted, isNrAvailable);
+ maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable);
+ updateNrStatus(mDataSpecificStates);
}
protected NetworkRegistrationState(Parcel source) {
@@ -448,6 +449,34 @@
dest.writeInt(mNrStatus);
}
+ /**
+ * Use the 5G NR Non-Standalone indicators from the network registration state to update the
+ * NR status. There are 3 indicators in the network registration state:
+ *
+ * 1. if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving cell.
+ * 2. if NR is supported by the selected PLMN.
+ * 3. if the use of dual connectivity with NR is restricted.
+ *
+ * The network has 5G NR capability if E-UTRA-NR Dual Connectivity is supported by the primary
+ * serving cell.
+ *
+ * The use of NR 5G is not restricted If the network has 5G NR capability and both the use of
+ * DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
+ * NR is restricted.
+ *
+ * @param state data specific registration state contains the 5G NR indicators.
+ */
+ private void updateNrStatus(DataSpecificRegistrationStates state) {
+ mNrStatus = NR_STATUS_NONE;
+ if (state.isEnDcAvailable) {
+ if (!state.isDcNrRestricted && state.isNrAvailable) {
+ mNrStatus = NR_STATUS_NOT_RESTRICTED;
+ } else {
+ mNrStatus = NR_STATUS_RESTRICTED;
+ }
+ }
+ }
+
public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
new Parcelable.Creator<NetworkRegistrationState>() {
@Override
diff --git a/telephony/java/android/telephony/NumberVerificationCallback.java b/telephony/java/android/telephony/NumberVerificationCallback.java
new file mode 100644
index 0000000..b00c573
--- /dev/null
+++ b/telephony/java/android/telephony/NumberVerificationCallback.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * A callback for number verification. After a request for number verification is received,
+ * the system will call {@link #onCallReceived(String)} if a phone call was received from a number
+ * matching the provided {@link PhoneNumberRange} or it will call {@link #onVerificationFailed(int)}
+ * if an error occurs.
+ * @hide
+ */
+@SystemApi
+public interface NumberVerificationCallback {
+ /** @hide */
+ @IntDef(value = {REASON_UNSPECIFIED, REASON_TIMED_OUT, REASON_NETWORK_NOT_AVAILABLE,
+ REASON_TOO_MANY_CALLS, REASON_CONCURRENT_REQUESTS, REASON_IN_ECBM,
+ REASON_IN_EMERGENCY_CALL},
+ prefix = {"REASON_"})
+ @interface NumberVerificationFailureReason {}
+
+ /**
+ * Verification failed for an unspecified reason.
+ */
+ int REASON_UNSPECIFIED = 0;
+
+ /**
+ * Verification failed because no phone call was received from a matching number within the
+ * provided timeout.
+ */
+ int REASON_TIMED_OUT = 1;
+
+ /**
+ * Verification failed because no cellular voice network is available.
+ */
+ int REASON_NETWORK_NOT_AVAILABLE = 2;
+
+ /**
+ * Verification failed because there are currently too many ongoing phone calls for a new
+ * incoming phone call to be received.
+ */
+ int REASON_TOO_MANY_CALLS = 3;
+
+ /**
+ * Verification failed because a previous request for verification has not yet completed.
+ */
+ int REASON_CONCURRENT_REQUESTS = 4;
+
+ /**
+ * Verification failed because the phone is in emergency callback mode.
+ */
+ int REASON_IN_ECBM = 5;
+
+ /**
+ * Verification failed because the phone is currently in an emergency call.
+ */
+ int REASON_IN_EMERGENCY_CALL = 6;
+
+ /**
+ * Called when the device receives a phone call from the provided {@link PhoneNumberRange}.
+ * @param phoneNumber The phone number within the range that called. May or may not contain the
+ * country code, but will be entirely numeric.
+ */
+ default void onCallReceived(@NonNull String phoneNumber) { }
+
+ /**
+ * Called when verification fails for some reason.
+ * @param reason The reason for failure.
+ */
+ default void onVerificationFailed(@NumberVerificationFailureReason int reason) { }
+}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/telephony/java/android/telephony/PhoneNumberRange.aidl
similarity index 81%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to telephony/java/android/telephony/PhoneNumberRange.aidl
index c66a6cb..b0727be 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/telephony/java/android/telephony/PhoneNumberRange.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package android.telephony;
-parcelable ContentCaptureEvent;
+parcelable PhoneNumberRange;
diff --git a/telephony/java/android/telephony/PhoneNumberRange.java b/telephony/java/android/telephony/PhoneNumberRange.java
new file mode 100644
index 0000000..dba803b
--- /dev/null
+++ b/telephony/java/android/telephony/PhoneNumberRange.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to represent a range of phone numbers. Each range corresponds to a contiguous
+ * block of phone numbers.
+ *
+ * Example:
+ * {@code
+ * {
+ * mCountryCode = "1"
+ * mPrefix = "650555"
+ * mLowerBound = "0055"
+ * mUpperBound = "0899"
+ * }
+ * }
+ * would match 16505550089 and 6505550472, but not 63827593759 or 16505550900
+ * @hide
+ */
+@SystemApi
+public final class PhoneNumberRange implements Parcelable {
+ public static final Creator<PhoneNumberRange> CREATOR = new Creator<PhoneNumberRange>() {
+ @Override
+ public PhoneNumberRange createFromParcel(Parcel in) {
+ return new PhoneNumberRange(in);
+ }
+
+ @Override
+ public PhoneNumberRange[] newArray(int size) {
+ return new PhoneNumberRange[size];
+ }
+ };
+
+ private final String mCountryCode;
+ private final String mPrefix;
+ private final String mLowerBound;
+ private final String mUpperBound;
+
+ /**
+ * @param countryCode The country code, omitting the leading "+"
+ * @param prefix A prefix that all numbers matching the range must have.
+ * @param lowerBound When concatenated with the prefix, represents the lower bound of phone
+ * numbers that match this range.
+ * @param upperBound When concatenated with the prefix, represents the upper bound of phone
+ * numbers that match this range.
+ */
+ public PhoneNumberRange(@NonNull String countryCode, @NonNull String prefix,
+ @NonNull String lowerBound, @NonNull String upperBound) {
+ validateLowerAndUpperBounds(lowerBound, upperBound);
+ if (!Pattern.matches("[0-9]*", countryCode)) {
+ throw new IllegalArgumentException("Country code must be all numeric");
+ }
+ if (!Pattern.matches("[0-9]*", prefix)) {
+ throw new IllegalArgumentException("Prefix must be all numeric");
+ }
+ mCountryCode = countryCode;
+ mPrefix = prefix;
+ mLowerBound = lowerBound;
+ mUpperBound = upperBound;
+ }
+
+ private PhoneNumberRange(Parcel in) {
+ mCountryCode = in.readStringNoHelper();
+ mPrefix = in.readStringNoHelper();
+ mLowerBound = in.readStringNoHelper();
+ mUpperBound = in.readStringNoHelper();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStringNoHelper(mCountryCode);
+ dest.writeStringNoHelper(mPrefix);
+ dest.writeStringNoHelper(mLowerBound);
+ dest.writeStringNoHelper(mUpperBound);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PhoneNumberRange that = (PhoneNumberRange) o;
+ return Objects.equals(mCountryCode, that.mCountryCode)
+ && Objects.equals(mPrefix, that.mPrefix)
+ && Objects.equals(mLowerBound, that.mLowerBound)
+ && Objects.equals(mUpperBound, that.mUpperBound);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCountryCode, mPrefix, mLowerBound, mUpperBound);
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneNumberRange{"
+ + "mCountryCode='" + mCountryCode + '\''
+ + ", mPrefix='" + mPrefix + '\''
+ + ", mLowerBound='" + mLowerBound + '\''
+ + ", mUpperBound='" + mUpperBound + '\''
+ + '}';
+ }
+
+ private void validateLowerAndUpperBounds(String lowerBound, String upperBound) {
+ if (lowerBound.length() != upperBound.length()) {
+ throw new IllegalArgumentException("Lower and upper bounds must have the same length");
+ }
+ if (!Pattern.matches("[0-9]*", lowerBound)) {
+ throw new IllegalArgumentException("Lower bound must be all numeric");
+ }
+ if (!Pattern.matches("[0-9]*", upperBound)) {
+ throw new IllegalArgumentException("Upper bound must be all numeric");
+ }
+ if (Integer.parseInt(lowerBound) > Integer.parseInt(upperBound)) {
+ throw new IllegalArgumentException("Lower bound must be lower than upper bound");
+ }
+ }
+
+ /**
+ * Checks to see if the provided phone number matches this range.
+ * @param number A phone number, with or without separators or a country code.
+ * @return {@code true} if the number matches, {@code false} otherwise.
+ */
+ public boolean matches(String number) {
+ // Check the prefix, make sure it matches either with or without the country code.
+ String normalizedNumber = number.replaceAll("[^0-9]", "");
+ String prefixWithCountryCode = mCountryCode + mPrefix;
+ String numberPostfix;
+ if (normalizedNumber.startsWith(prefixWithCountryCode)) {
+ numberPostfix = normalizedNumber.substring(prefixWithCountryCode.length());
+ } else if (normalizedNumber.startsWith(mPrefix)) {
+ numberPostfix = normalizedNumber.substring(mPrefix.length());
+ } else {
+ return false;
+ }
+
+ // Next check the postfix to make sure it lies within the bounds.
+ try {
+ int lower = Integer.parseInt(mLowerBound);
+ int upper = Integer.parseInt(mUpperBound);
+ int numberToCheck = Integer.parseInt(numberPostfix);
+ return numberToCheck <= upper && numberToCheck >= lower;
+ } catch (NumberFormatException e) {
+ Log.e(PhoneNumberRange.class.getSimpleName(), "Invalid bounds or number.", e);
+ return false;
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index f6e8d34..c95837e 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1738,7 +1738,10 @@
* @param number the number to look up.
* @return true if the number is in the list of emergency numbers
* listed in the RIL / SIM, otherwise return false.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} instead.
*/
+ @Deprecated
public static boolean isEmergencyNumber(String number) {
return isEmergencyNumber(getDefaultVoiceSubId(), number);
}
@@ -1751,8 +1754,13 @@
* @param number the number to look up.
* @return true if the number is in the list of emergency numbers
* listed in the RIL / SIM, otherwise return false.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isEmergencyNumber(int subId, String number) {
// Return true only if the specified number *exactly* matches
@@ -1778,8 +1786,12 @@
* listed in the RIL / SIM, *or* if the number starts with the
* same digits as any of those emergency numbers.
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isPotentialEmergencyNumber(String number) {
return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
}
@@ -1802,9 +1814,14 @@
* @return true if the number is in the list of emergency numbers
* listed in the RIL / SIM, *or* if the number starts with the
* same digits as any of those emergency numbers.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
@UnsupportedAppUsage
+ @Deprecated
public static boolean isPotentialEmergencyNumber(int subId, String number) {
// Check against the emergency numbers listed by the RIL / SIM,
// and *don't* require an exact match.
@@ -1867,8 +1884,12 @@
* @return if the number is an emergency number for the specific country, then return true,
* otherwise false
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
@@ -1882,8 +1903,13 @@
* @param defaultCountryIso the specific country which the number should be checked against
* @return if the number is an emergency number for the specific country, then return true,
* otherwise false
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
return isEmergencyNumberInternal(subId, number,
defaultCountryIso,
@@ -1909,8 +1935,12 @@
* country, *or* if the number starts with the same digits as
* any of those emergency numbers.
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
}
@@ -1934,8 +1964,13 @@
* @return true if the number is an emergency number for the specific
* country, *or* if the number starts with the same digits as
* any of those emergency numbers.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isPotentialEmergencyNumber(int subId, String number,
String defaultCountryIso) {
return isEmergencyNumberInternal(subId, number,
@@ -1983,92 +2018,7 @@
private static boolean isEmergencyNumberInternal(int subId, String number,
String defaultCountryIso,
boolean useExactMatch) {
- // If the number passed in is null, just return false:
- if (number == null) return false;
-
- // If the number passed in is a SIP address, return false, since the
- // concept of "emergency numbers" is only meaningful for calls placed
- // over the cell network.
- // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
- // since the whole point of extractNetworkPortionAlt() is to filter out
- // any non-dialable characters (which would turn 'abc911def@example.com'
- // into '911', for example.))
- if (isUriNumber(number)) {
- return false;
- }
-
- // Strip the separators from the number before comparing it
- // to the list.
- number = extractNetworkPortionAlt(number);
-
- String emergencyNumbers = "";
- int slotId = SubscriptionManager.getSlotIndex(subId);
-
- // retrieve the list of emergency numbers
- // check read-write ecclist property first
- String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-
- emergencyNumbers = SystemProperties.get(ecclist, "");
-
- Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
- + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers);
-
- if (TextUtils.isEmpty(emergencyNumbers)) {
- // then read-only ecclist property since old RIL only uses this
- emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
- }
-
- if (!TextUtils.isEmpty(emergencyNumbers)) {
- // searches through the comma-separated list for a match,
- // return true if one is found.
- for (String emergencyNum : emergencyNumbers.split(",")) {
- // It is not possible to append additional digits to an emergency number to dial
- // the number in Brazil - it won't connect.
- if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
- if (number.equals(emergencyNum)) {
- return true;
- }
- } else {
- if (number.startsWith(emergencyNum)) {
- return true;
- }
- }
- }
- // no matches found against the list!
- return false;
- }
-
- Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
- + " Use embedded logic for determining ones.");
-
- // If slot id is invalid, means that there is no sim card.
- // According spec 3GPP TS22.101, the following numbers should be
- // ECC numbers when SIM/USIM is not present.
- emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
-
- for (String emergencyNum : emergencyNumbers.split(",")) {
- if (useExactMatch) {
- if (number.equals(emergencyNum)) {
- return true;
- }
- } else {
- if (number.startsWith(emergencyNum)) {
- return true;
- }
- }
- }
-
- // No ecclist system property, so use our own list.
- if (defaultCountryIso != null) {
- ShortNumberInfo info = ShortNumberInfo.getInstance();
- if (useExactMatch) {
- return info.isEmergencyNumber(number, defaultCountryIso);
- } else {
- return info.connectsToEmergencyNumber(number, defaultCountryIso);
- }
- }
-
- return false;
+ return TelephonyManager.getDefault().isCurrentEmergencyNumber(number);
}
/**
@@ -2078,7 +2028,11 @@
* @param context the specific context which the number should be checked against
* @return true if the specified number is an emergency number for the country the user
* is currently in.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
*/
+ @Deprecated
public static boolean isLocalEmergencyNumber(Context context, String number) {
return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
}
@@ -2091,8 +2045,13 @@
* @param context the specific context which the number should be checked against
* @return true if the specified number is an emergency number for the country the user
* is currently in.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
return isLocalEmergencyNumberInternal(subId, number,
@@ -2120,8 +2079,13 @@
* CountryDetector.
*
* @see android.location.CountryDetector
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
@@ -2147,9 +2111,13 @@
* @return true if the specified number is an emergency number for a local country, based on the
* CountryDetector.
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
@UnsupportedAppUsage
+ @Deprecated
public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
String number) {
return isLocalEmergencyNumberInternal(subId, number,
@@ -2217,6 +2185,101 @@
}
/**
+ * Back-up old logics for {@link #isEmergencyNumberInternal} for legacy and deprecate purpose.
+ *
+ * @hide
+ */
+ public static boolean isEmergencyNumberInternal(String number, boolean useExactMatch,
+ String defaultCountryIso) {
+ // If the number passed in is null, just return false:
+ if (number == null) return false;
+
+ // If the number passed in is a SIP address, return false, since the
+ // concept of "emergency numbers" is only meaningful for calls placed
+ // over the cell network.
+ // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
+ // since the whole point of extractNetworkPortionAlt() is to filter out
+ // any non-dialable characters (which would turn 'abc911def@example.com'
+ // into '911', for example.))
+ if (PhoneNumberUtils.isUriNumber(number)) {
+ return false;
+ }
+
+ // Strip the separators from the number before comparing it
+ // to the list.
+ number = PhoneNumberUtils.extractNetworkPortionAlt(number);
+
+ String emergencyNumbers = "";
+ int slotId = SubscriptionManager.getSlotIndex(getDefaultVoiceSubId());
+
+ // retrieve the list of emergency numbers
+ // check read-write ecclist property first
+ String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
+
+ emergencyNumbers = SystemProperties.get(ecclist, "");
+
+ Rlog.d(LOG_TAG, "slotId:" + slotId + " country:"
+ + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers);
+
+ if (TextUtils.isEmpty(emergencyNumbers)) {
+ // then read-only ecclist property since old RIL only uses this
+ emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
+ }
+
+ if (!TextUtils.isEmpty(emergencyNumbers)) {
+ // searches through the comma-separated list for a match,
+ // return true if one is found.
+ for (String emergencyNum : emergencyNumbers.split(",")) {
+ // It is not possible to append additional digits to an emergency number to dial
+ // the number in Brazil - it won't connect.
+ if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
+ }
+ }
+ // no matches found against the list!
+ return false;
+ }
+
+ Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
+ + " Use embedded logic for determining ones.");
+
+ // If slot id is invalid, means that there is no sim card.
+ // According spec 3GPP TS22.101, the following numbers should be
+ // ECC numbers when SIM/USIM is not present.
+ emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
+
+ for (String emergencyNum : emergencyNumbers.split(",")) {
+ if (useExactMatch) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
+ }
+ }
+
+ // No ecclist system property, so use our own list.
+ if (defaultCountryIso != null) {
+ ShortNumberInfo info = ShortNumberInfo.getInstance();
+ if (useExactMatch) {
+ return info.isEmergencyNumber(number, defaultCountryIso);
+ } else {
+ return info.connectsToEmergencyNumber(number, defaultCountryIso);
+ }
+ }
+
+ return false;
+ }
+
+ /**
* isVoiceMailNumber: checks a given number against the voicemail
* number provided by the RIL and SIM card. The caller must have
* the READ_PHONE_STATE credential.
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e8a28ca..0df0daf 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -27,12 +27,14 @@
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
+import android.telephony.emergency.EmergencyNumber;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IPhoneStateListener;
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -313,6 +315,8 @@
*
* <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
* app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see #onEmergencyNumberListChanged
*/
public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
@@ -603,6 +607,21 @@
}
/**
+ * Callback invoked when the current emergency number list has changed
+ *
+ * @param emergencyNumberList Map including the key as the active subscription ID
+ * (Note: if there is no active subscription, the key is
+ * {@link SubscriptionManager#getDefaultSubscriptionId})
+ * and the value as the list of {@link EmergencyNumber};
+ * null if this information is not available.
+ * @hide
+ */
+ public void onEmergencyNumberListChanged(
+ @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when OEM hook raw event is received. Requires
* the READ_PRIVILEGED_PHONE_STATE permission.
* @param rawData is the byte array of the OEM hook raw data.
@@ -859,6 +878,16 @@
() -> psl.onPhysicalChannelConfigurationChanged(configs)));
}
+ @Override
+ public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
+ }
+
public void onPhoneCapabilityChanged(PhoneCapability capability) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 13fbeaa..78f05168 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -27,6 +27,7 @@
import android.os.Parcelable;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.NetworkRegistrationState.Domain;
+import android.telephony.NetworkRegistrationState.NRStatus;
import android.text.TextUtils;
import java.lang.annotation.Retention;
@@ -118,6 +119,13 @@
*/
public static final int FREQUENCY_RANGE_MMWAVE = 4;
+ private static final List<Integer> FREQUENCY_RANGE_ORDER = Arrays.asList(
+ FREQUENCY_RANGE_UNKNOWN,
+ FREQUENCY_RANGE_LOW,
+ FREQUENCY_RANGE_MID,
+ FREQUENCY_RANGE_HIGH,
+ FREQUENCY_RANGE_MMWAVE);
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "DUPLEX_MODE_",
@@ -1351,6 +1359,18 @@
}
/**
+ * Get the NR 5G status of the mobile data network.
+ * @return the NR 5G status.
+ * @hide
+ */
+ public @NRStatus int getNrStatus() {
+ final NetworkRegistrationState regState = getNetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN);
+ if (regState == null) return NetworkRegistrationState.NR_STATUS_NONE;
+ return regState.getNrStatus();
+ }
+
+ /**
* @param nrFrequencyRange the frequency range of 5G NR.
* @hide
*/
@@ -1403,47 +1423,49 @@
}
/** @hide */
- public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rt) {
- switch(rt) {
- case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
- return TelephonyManager.NETWORK_TYPE_GPRS;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
- return TelephonyManager.NETWORK_TYPE_EDGE;
- case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
- return TelephonyManager.NETWORK_TYPE_UMTS;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
- return TelephonyManager.NETWORK_TYPE_HSDPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
- return TelephonyManager.NETWORK_TYPE_HSUPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
- return TelephonyManager.NETWORK_TYPE_HSPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
- case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
- return TelephonyManager.NETWORK_TYPE_CDMA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
- return TelephonyManager.NETWORK_TYPE_1xRTT;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
- return TelephonyManager.NETWORK_TYPE_EVDO_0;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
- return TelephonyManager.NETWORK_TYPE_EVDO_A;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
- return TelephonyManager.NETWORK_TYPE_EVDO_B;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
- return TelephonyManager.NETWORK_TYPE_EHRPD;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
- return TelephonyManager.NETWORK_TYPE_LTE;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
- return TelephonyManager.NETWORK_TYPE_HSPAP;
- case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
- return TelephonyManager.NETWORK_TYPE_GSM;
- case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
- return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
- return TelephonyManager.NETWORK_TYPE_IWLAN;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
- return TelephonyManager.NETWORK_TYPE_LTE_CA;
- default:
- return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rat) {
+ switch(rat) {
+ case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
+ return TelephonyManager.NETWORK_TYPE_GPRS;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
+ return TelephonyManager.NETWORK_TYPE_EDGE;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
+ return TelephonyManager.NETWORK_TYPE_UMTS;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
+ return TelephonyManager.NETWORK_TYPE_HSDPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
+ return TelephonyManager.NETWORK_TYPE_HSUPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
+ return TelephonyManager.NETWORK_TYPE_HSPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
+ return TelephonyManager.NETWORK_TYPE_CDMA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
+ return TelephonyManager.NETWORK_TYPE_1xRTT;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
+ return TelephonyManager.NETWORK_TYPE_EVDO_0;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
+ return TelephonyManager.NETWORK_TYPE_EVDO_A;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
+ return TelephonyManager.NETWORK_TYPE_EVDO_B;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
+ return TelephonyManager.NETWORK_TYPE_EHRPD;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+ return TelephonyManager.NETWORK_TYPE_LTE;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
+ return TelephonyManager.NETWORK_TYPE_HSPAP;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
+ return TelephonyManager.NETWORK_TYPE_GSM;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
+ return TelephonyManager.NETWORK_TYPE_LTE_CA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_NR:
+ return TelephonyManager.NETWORK_TYPE_NR;
+ default:
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
}
@@ -1524,7 +1546,6 @@
}
}
-
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public @TelephonyManager.NetworkType int getDataNetworkType() {
@@ -1835,4 +1856,13 @@
mNetworkRegistrationStates.add(regState);
}
}
+
+ /**
+ * @hide
+ */
+ public static final int getBetterNRFrequencyRange(int range1, int range2) {
+ return FREQUENCY_RANGE_ORDER.indexOf(range1) > FREQUENCY_RANGE_ORDER.indexOf(range2)
+ ? range1
+ : range2;
+ }
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index ea408bf..1b37bad 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -365,7 +365,6 @@
*
* @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
*/
- @SystemApi
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(allOf = {
android.Manifest.permission.MODIFY_PHONE_STATE,
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index b41e14e..dacc5d8 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -150,6 +150,19 @@
private String mGroupUUID;
/**
+ * A property in opportunistic subscription to indicate whether it is metered or not.
+ */
+ private boolean mIsMetered;
+
+ /**
+ * Whether group of the subscription is disabled.
+ * This is only useful if it's a grouped opportunistic subscription. In this case, if all
+ * primary (non-opportunistic) subscriptions in the group are deactivated (unplugged pSIM
+ * or deactivated eSIM profile), we should disable this opportunistic subscription.
+ */
+ private boolean mIsGroupDisabled = false;
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
@@ -158,7 +171,7 @@
@Nullable UiccAccessRule[] accessRules, String cardId) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
- false, null);
+ false, null, true);
}
/**
@@ -168,7 +181,19 @@
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
- @Nullable String groupUUID) {
+ @Nullable String groupUUID, boolean isMetered) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
+ isOpportunistic, groupUUID, isMetered, false);
+ }
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
+ @Nullable String groupUUID, boolean isMetered, boolean isGroupDisabled) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -187,8 +212,11 @@
this.mCardId = cardId;
this.mIsOpportunistic = isOpportunistic;
this.mGroupUUID = groupUUID;
+ this.mIsMetered = isMetered;
+ this.mIsGroupDisabled = isGroupDisabled;
}
+
/**
* @return the subscription ID.
*/
@@ -403,6 +431,18 @@
}
/**
+ * Used in opportunistic subscription ({@link #isOpportunistic()}) to indicate whether it's
+ * metered or not.This is one of the factors when deciding to switch to the subscription.
+ * (a non-metered subscription, for example, would likely be preferred over a metered one).
+ *
+ * @return whether subscription is metered.
+ * @hide
+ */
+ public boolean isMetered() {
+ return mIsMetered;
+ }
+
+ /**
* Checks whether the app with the given context is authorized to manage this subscription
* according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
* returns true).
@@ -475,6 +515,22 @@
return this.mCardId;
}
+ /**
+ * Set whether the subscription's group is disabled.
+ * @hide
+ */
+ public void setGroupDisabled(boolean isGroupDisabled) {
+ this.mIsGroupDisabled = isGroupDisabled;
+ }
+
+ /**
+ * Return whether the subscription's group is disabled.
+ * @hide
+ */
+ public boolean isGroupDisabled() {
+ return mIsGroupDisabled;
+ }
+
public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
@Override
public SubscriptionInfo createFromParcel(Parcel source) {
@@ -496,10 +552,13 @@
String cardId = source.readString();
boolean isOpportunistic = source.readBoolean();
String groupUUID = source.readString();
+ boolean isMetered = source.readBoolean();
+ boolean isGroupDisabled = source.readBoolean();
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules, cardId, isOpportunistic, groupUUID);
+ isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered,
+ isGroupDisabled);
}
@Override
@@ -528,6 +587,8 @@
dest.writeString(mCardId);
dest.writeBoolean(mIsOpportunistic);
dest.writeString(mGroupUUID);
+ dest.writeBoolean(mIsMetered);
+ dest.writeBoolean(mIsGroupDisabled);
}
@Override
@@ -561,14 +622,15 @@
+ " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+ " accessRules " + Arrays.toString(mAccessRules)
+ " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
- + " mGroupUUID=" + mGroupUUID + "}";
+ + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered
+ + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
}
@Override
public int hashCode() {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
- mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso,
- mCardId, mDisplayName, mCarrierName, mAccessRules);
+ mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
+ mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules, mIsGroupDisabled);
}
@Override
@@ -590,7 +652,9 @@
&& mDataRoaming == toCompare.mDataRoaming
&& mIsEmbedded == toCompare.mIsEmbedded
&& mIsOpportunistic == toCompare.mIsOpportunistic
+ && mIsGroupDisabled == toCompare.mIsGroupDisabled
&& Objects.equals(mGroupUUID, toCompare.mGroupUUID)
+ && mIsMetered == toCompare.mIsMetered
&& Objects.equals(mIccId, toCompare.mIccId)
&& Objects.equals(mNumber, toCompare.mNumber)
&& Objects.equals(mMcc, toCompare.mMcc)
@@ -601,4 +665,4 @@
&& TextUtils.equals(mCarrierName, toCompare.mCarrierName)
&& Arrays.equals(mAccessRules, toCompare.mAccessRules);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 45cfe1e..387453f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -19,6 +19,7 @@
import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
@@ -52,6 +53,7 @@
import android.os.ServiceManager;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -67,6 +69,7 @@
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* SubscriptionManager is the application interface to SubscriptionController
@@ -407,6 +410,13 @@
public static final String MNC = "mnc";
/**
+ * TelephonyProvider column name for the iso country code associated with a SIM.
+ * <P>Type: TEXT (String)</P>
+ * @hide
+ */
+ public static final String ISO_COUNTRY_CODE = "iso_country_code";
+
+ /**
* TelephonyProvider column name for the sim provisioning status associated with a SIM.
* <P>Type: INTEGER (int)</P>
* @hide
@@ -576,7 +586,12 @@
* @hide
*/
public static final String GROUP_UUID = "group_uuid";
-
+ /**
+ * TelephonyProvider column name for whether a subscription is metered or not, that is, whether
+ * the network it connects to charges for subscription or not. For example, paid CBRS or unpaid.
+ * @hide
+ */
+ public static final String IS_METERED = "is_metered";
/**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
@@ -621,7 +636,6 @@
* the user is interested in.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- @SystemApi
public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS
= "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
@@ -641,7 +655,6 @@
* {@code android.permission.MANAGE_SUBSCRIPTION_PLANS} permission.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @SystemApi
public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS
= "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
@@ -1578,14 +1591,23 @@
return subId;
}
- /** @hide */
- @UnsupportedAppUsage
- public void setDefaultSmsSubId(int subId) {
- if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
+ /**
+ * Set the subscription which will be used by default for SMS, with the subscription which
+ * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+ * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+ *
+ * @param subscriptionId the supplied subscription ID
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDefaultSmsSubId(int subscriptionId) {
+ if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- iSub.setDefaultSmsSubId(subId);
+ iSub.setDefaultSmsSubId(subscriptionId);
}
} catch (RemoteException ex) {
// ignore it
@@ -1633,14 +1655,23 @@
return subId;
}
- /** @hide */
- @UnsupportedAppUsage
- public void setDefaultDataSubId(int subId) {
- if (VDBG) logd("setDataSubscription sub id = " + subId);
+ /**
+ * Set the subscription which will be used by default for data, with the subscription which
+ * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+ * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+ *
+ * @param subscriptionId the supplied subscription ID
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDefaultDataSubId(int subscriptionId) {
+ if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- iSub.setDefaultDataSubId(subId);
+ iSub.setDefaultDataSubId(subscriptionId);
}
} catch (RemoteException ex) {
// ignore it
@@ -2029,7 +2060,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
try {
SubscriptionPlan[] subscriptionPlans =
@@ -2061,7 +2091,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
try {
getNetworkPolicy().setSubscriptionPlans(subId,
@@ -2103,7 +2132,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@DurationMillisLong long timeoutMillis) {
try {
@@ -2139,7 +2167,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@DurationMillisLong long timeoutMillis) {
try {
@@ -2375,16 +2402,21 @@
* together, some of them may be invisible to the users, etc.
*
* Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or can manage all subscriptions in the list, according to their
- * acess rules.
+ * permission or had carrier privilege permission on the subscriptions:
+ * {@link TelephonyManager#hasCarrierPrivileges()} or
+ * {@link #canManageSubscription(SubscriptionInfo)}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
*
* @param subIdList list of subId that will be in the same group
* @return groupUUID a UUID assigned to the subscription group. It returns
* null if fails.
*
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public String setSubscriptionGroup(int[] subIdList) {
+ public @Nullable String setSubscriptionGroup(@NonNull int[] subIdList) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (VDBG) {
logd("[setSubscriptionGroup]+ subIdList:" + Arrays.toString(subIdList));
@@ -2403,6 +2435,131 @@
return groupUUID;
}
+ /**
+ * Remove a list of subscriptions from their subscription group.
+ * See {@link #setSubscriptionGroup(int[])} for more details.
+ *
+ * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * permission or had carrier privilege permission on the subscriptions:
+ * {@link TelephonyManager#hasCarrierPrivileges()} or
+ * {@link #canManageSubscription(SubscriptionInfo)}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ *
+ * @param subIdList list of subId that need removing from their groups.
+ * @return whether the operation succeeds.
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean removeSubscriptionsFromGroup(@NonNull int[] subIdList) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (VDBG) {
+ logd("[removeSubscriptionsFromGroup]+ subIdList:" + Arrays.toString(subIdList));
+ }
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.removeSubscriptionsFromGroup(subIdList, pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return false;
+ }
+
+ /**
+ * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
+ * See {@link #setSubscriptionGroup(int[])} for more details.
+ *
+ * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
+ * permission or had carrier privilege permission on the subscription.
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ *
+ * @param subId of which list of subInfo from the same group will be returned.
+ * @return list of subscriptionInfo that belong to the same group, including the given
+ * subscription itself. It will return null if the subscription doesn't exist or it
+ * doesn't belong to any group.
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public @Nullable List<SubscriptionInfo> getSubscriptionsInGroup(int subId) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (VDBG) {
+ logd("[getSubscriptionsInGroup]+ subId:" + subId);
+ }
+
+ List<SubscriptionInfo> result = null;
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getSubscriptionsInGroup(subId, pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
+ * Set metered by simInfo index
+ *
+ * @param isMetered whether it’s a metered subscription.
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public int setMetered(boolean isMetered, int subId) {
+ if (VDBG) logd("[setIsMetered]+ isMetered:" + isMetered + " subId:" + subId);
+ return setSubscriptionPropertyHelper(subId, "setIsMetered",
+ (iSub)-> iSub.setMetered(isMetered, subId));
+ }
+
+ /**
+ * Whether system UI should hide a subscription. If it's a bundled opportunistic
+ * subscription, it shouldn't show up in anywhere in Settings app, dialer app,
+ * or status bar.
+ *
+ * @param info the subscriptionInfo to check against.
+ * @return true if this subscription should be hidden.
+ *
+ * @hide
+ */
+ public static boolean shouldHideSubscription(SubscriptionInfo info) {
+ return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic());
+ }
+
+ /**
+ * Return a list of subscriptions that are available and visible to the user.
+ * Used by Settings app to show a list of subscriptions for user to pick.
+ *
+ * <p>
+ * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+ * for getSelectableSubscriptionInfoList to be invoked.
+ * @return list of user selectable subscriptions.
+ *
+ * @hide
+ */
+ public @Nullable List<SubscriptionInfo> getSelectableSubscriptionInfoList() {
+ List<SubscriptionInfo> availableList = getAvailableSubscriptionInfoList();
+ if (availableList == null) {
+ return null;
+ } else {
+ return getAvailableSubscriptionInfoList().stream()
+ .filter(subInfo -> !shouldHideSubscription(subInfo))
+ .collect(Collectors.toList());
+ }
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index e8bbe42..d67169c 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -45,7 +45,6 @@
* @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
* @see SubscriptionManager#getSubscriptionPlans(int)
*/
-@SystemApi
public final class SubscriptionPlan implements Parcelable {
/** {@hide} */
@IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7a6647c..348ab2a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -77,6 +77,7 @@
import com.android.internal.telecom.ITelecomService;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IAns;
+import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -224,6 +225,13 @@
@SystemApi
public static final int SRVCC_STATE_HANDOVER_CANCELED = 3;
+ /**
+ * An invalid card identifier.
+ * @hide
+ */
+ @SystemApi
+ public static final int INVALID_CARD_ID = -1;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"SRVCC_STATE_"},
@@ -809,6 +817,7 @@
* @see TelephonyManager#NETWORK_TYPE_LTE
* @see TelephonyManager#NETWORK_TYPE_EHRPD
* @see TelephonyManager#NETWORK_TYPE_HSPAP
+ * @see TelephonyManager#NETWORK_TYPE_NR
*
* <p class="note">
* Retrieve with
@@ -1215,81 +1224,79 @@
"android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
/**
- * Broadcast Action: The subscription precise carrier identity has changed.
- * Similar like {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}, this intent will be sent
- * on the event of {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}. However, its possible
- * that precise carrier identity changes while
- * {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same e.g, the same
- * subscription switches to different IMSI could potentially change its precise carrier id.
- *
- * The intent will have the following extra values:
- * <ul>
- * <li>{@link #EXTRA_PRECISE_CARRIER_ID} The up-to-date precise carrier id of the
- * current subscription.
- * </li>
- * <li>{@link #EXTRA_PRECISE_CARRIER_NAME} The up-to-date carrier name of the current
- * subscription.
- * </li>
- * <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
- * identity.
- * </li>
- * </ul>
- * <p class="note">This is a protected intent that can only be sent by the system.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED =
- "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
-
- /**
* An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
- * the updated carrier id {@link TelephonyManager#getSimCarrierId()} of
- * the current subscription.
+ * the updated carrier id returned by {@link TelephonyManager#getSimCarrierId()}.
* <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
* the carrier cannot be identified.
*/
public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
/**
- * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
- * the updated mno carrier id of the current subscription.
- * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
- * the carrier cannot be identified.
- *
- *@hide
- */
- public static final String EXTRA_MNO_CARRIER_ID = "android.telephony.extra.MNO_CARRIER_ID";
-
- /**
* An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
* indicates the updated carrier name of the current subscription.
- * {@see TelephonyManager#getSimCarrierIdName()}
+ * @see TelephonyManager#getSimCarrierIdName()
* <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
* usually the brand name of the subsidiary (e.g. T-Mobile).
*/
public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
/**
+ * Broadcast Action: The subscription precise carrier identity has changed.
+ * The precise carrier id can be used to further differentiate a carrier by different
+ * networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
+ * carrier id returned by {@link #getSimCarrierId()} but could have multiple precise carrier id.
+ * e.g, {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM,
+ * while {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based
+ * on the current subscription IMSI. For carriers without any fine-grained ids, precise carrier
+ * id is same as carrier id.
+ *
+ * <p>Similar like {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}, this intent will be
+ * sent on the event of {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} while its also
+ * possible to be sent without {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} when
+ * precise carrier id changes with the same carrier id.
+ * e.g, the same subscription switches to different IMSI could potentially change its
+ * precise carrier id while carrier id remains the same.
+ * @see #getSimPreciseCarrierId()
+ * @see #getSimCarrierId()
+ *
+ * The intent will have the following extra values:
+ * <ul>
+ * <li>{@link #EXTRA_PRECISE_CARRIER_ID} The up-to-date precise carrier id of the
+ * current subscription.
+ * </li>
+ * <li>{@link #EXTRA_PRECISE_CARRIER_NAME} The up-to-date name of the precise carrier id.
+ * </li>
+ * <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
+ * identity.
+ * </li>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED =
+ "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
+
+ /**
* An int extra used with {@link #ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED} which
- * indicates the updated precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()} of
- * the current subscription. Note, its possible precise carrier id changes while
- * {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same e.g, when
- * subscription switch to different IMSI.
+ * indicates the updated precise carrier id returned by
+ * {@link TelephonyManager#getSimPreciseCarrierId()}. Note, its possible precise carrier id
+ * changes while {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same
+ * e.g, when subscription switch to different IMSIs.
* <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
* the carrier cannot be identified.
- * @hide
*/
public static final String EXTRA_PRECISE_CARRIER_ID =
"android.telephony.extra.PRECISE_CARRIER_ID";
/**
* An string extra used with {@link #ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED} which
- * indicates the updated precise carrier name of the current subscription.
- * {@see TelephonyManager#getSimPreciseCarrierIdName()}
- * <p>it's a user-facing name of the precise carrier id {@link #EXTRA_PRECISE_CARRIER_ID},
- * @hide
+ * indicates the updated precise carrier name returned by
+ * {@link TelephonyManager#getSimPreciseCarrierIdName()}.
+ * <p>it's a user-facing name of the precise carrier id {@link #EXTRA_PRECISE_CARRIER_ID}, e.g,
+ * Tracfone-AT&T.
*/
- public static final String EXTRA_PRECISE_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
+ public static final String EXTRA_PRECISE_CARRIER_NAME =
+ "android.telephony.extra.PRECISE_CARRIER_NAME";
/**
* An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
@@ -1341,6 +1348,13 @@
*/
public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
+ /**
+ * The max value for the timeout passed in {@link #requestNumberVerification}.
+ * @hide
+ */
+ @SystemApi
+ public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
+
//
//
// Device Info
@@ -2320,6 +2334,7 @@
* @see #NETWORK_TYPE_LTE
* @see #NETWORK_TYPE_EHRPD
* @see #NETWORK_TYPE_HSPAP
+ * @see #NETWORK_TYPE_NR
*
* @hide
*/
@@ -2371,6 +2386,7 @@
* @see #NETWORK_TYPE_LTE
* @see #NETWORK_TYPE_EHRPD
* @see #NETWORK_TYPE_HSPAP
+ * @see #NETWORK_TYPE_NR
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -2557,6 +2573,8 @@
return "IWLAN";
case NETWORK_TYPE_LTE_CA:
return "LTE_CA";
+ case NETWORK_TYPE_NR:
+ return "NR";
default:
return "UNKNOWN";
}
@@ -3130,6 +3148,34 @@
}
/**
+ * Get the card ID of the default eUICC card. If there is no eUICC, returns
+ * {@link #INVALID_CARD_ID}.
+ *
+ * <p>The card ID is a unique identifier associated with a UICC or eUICC card. Card IDs are
+ * unique to a device, and always refer to the same UICC or eUICC card unless the device goes
+ * through a factory reset.
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @return card ID of the default eUICC card.
+ * @hide
+ */
+ @SystemApi
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getCardIdForDefaultEuicc() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return INVALID_CARD_ID;
+ }
+ return telephony.getCardIdForDefaultEuicc(mSubId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ return INVALID_CARD_ID;
+ }
+ }
+
+ /**
* Gets all the UICC slots. The objects in the array can be null if the slot info is not
* available, which is possible between phone process starting and getting slot info from modem.
*
@@ -5503,6 +5549,73 @@
}
/**
+ * Request that the next incoming call from a number matching {@code range} be intercepted.
+ *
+ * This API is intended for OEMs to provide a service for apps to verify the device's phone
+ * number. When called, the Telephony stack will store the provided {@link PhoneNumberRange} and
+ * intercept the next incoming call from a number that lies within the range, within a timeout
+ * specified by {@code timeoutMillis}.
+ *
+ * If such a phone call is received, the caller will be notified via
+ * {@link NumberVerificationCallback#onCallReceived(String)} on the provided {@link Executor}.
+ * If verification fails for any reason, the caller will be notified via
+ * {@link NumberVerificationCallback#onVerificationFailed(int)}
+ * on the provided {@link Executor}.
+ *
+ * In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of this
+ * API must also be listed in the device configuration as an authorized app in
+ * {@code packages/services/Telephony/res/values/config.xml} under the
+ * {@code config_number_verification_package_name} key.
+ *
+ * @hide
+ * @param range The range of phone numbers the caller expects a phone call from.
+ * @param timeoutMillis The amount of time to wait for such a call, or
+ * {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser.
+ * @param executor The {@link Executor} that callbacks should be executed on.
+ * @param callback The callback to use for delivering results.
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull NumberVerificationCallback callback) {
+ if (executor == null) {
+ throw new NullPointerException("Executor must be non-null");
+ }
+ if (callback == null) {
+ throw new NullPointerException("Callback must be non-null");
+ }
+
+ INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() {
+ @Override
+ public void onCallReceived(String phoneNumber) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onCallReceived(phoneNumber)));
+ }
+
+ @Override
+ public void onVerificationFailed(int reason) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onVerificationFailed(reason)));
+ }
+ };
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.requestNumberVerification(range, timeoutMillis, internalCallback,
+ getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "requestNumberVerification RemoteException", ex);
+ executor.execute(() ->
+ callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED));
+ }
+ }
+
+ /**
* Sets a per-phone telephony property with the value specified.
*
* @hide
@@ -6552,7 +6665,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCarrierPrivilegeStatus(mSubId) ==
+ return telephony.getCarrierPrivilegeStatus(subId) ==
CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
} catch (RemoteException ex) {
@@ -8438,7 +8551,7 @@
/**
* Returns carrier id name of the current subscription.
- * <p>Carrier id name is a user-facing name of carrier id
+ * <p>Carrier id name is a user-facing name of carrier id returned by
* {@link #getSimCarrierId()}, usually the brand name of the subsidiary
* (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
* should have a single carrier name. Carrier name is not a canonical identity,
@@ -8448,7 +8561,7 @@
* @return Carrier name of the current subscription. Return {@code null} if the subscription is
* unavailable or the carrier cannot be identified.
*/
- public CharSequence getSimCarrierIdName() {
+ public @Nullable CharSequence getSimCarrierIdName() {
try {
ITelephony service = getITelephony();
if (service != null) {
@@ -8465,10 +8578,10 @@
*
* <p>The precise carrier id can be used to further differentiate a carrier by different
* networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
- * carrier id {@link #getSimCarrierId()} but can have multiple precise carrier id. e.g,
- * {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM, while
- * {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based on the
- * current subscription IMSI.
+ * carrier id returned by {@link #getSimCarrierId()} but could have multiple precise carrier id.
+ * e.g, {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM,
+ * while {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based
+ * on the current subscription IMSI.
*
* <p>For carriers without any fine-grained carrier ids, return {@link #getSimCarrierId()}
* <p>Precise carrier ids are defined in the same way as carrier id
@@ -8478,8 +8591,6 @@
* @return Returns fine-grained carrier id of the current subscription.
* Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
* be identified.
- *
- * @hide
*/
public int getSimPreciseCarrierId() {
try {
@@ -8495,16 +8606,14 @@
/**
* Similar like {@link #getSimCarrierIdName()}, returns user-facing name of the
- * precise carrier id {@link #getSimPreciseCarrierId()}
+ * precise carrier id returned by {@link #getSimPreciseCarrierId()}.
*
* <p>The returned name is unlocalized.
*
* @return user-facing name of the subscription precise carrier id. Return {@code null} if the
* subscription is unavailable or the carrier cannot be identified.
- *
- * @hide
*/
- public CharSequence getSimPreciseCarrierIdName() {
+ public @Nullable CharSequence getSimPreciseCarrierIdName() {
try {
ITelephony service = getITelephony();
if (service != null) {
@@ -8517,6 +8626,62 @@
}
/**
+ * Returns carrier id based on sim MCCMNC (returned by {@link #getSimOperator()}) only.
+ * This is used for fallback when configurations/logic for exact carrier id
+ * {@link #getSimCarrierId()} are not found.
+ *
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
+ * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
+ * by default. After carrier id table update, a new carrier id was assigned. If apps don't
+ * take the update with the new id, it might be helpful to always fallback by using carrier
+ * id based on MCCMNC if there is no match.
+ *
+ * @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
+ * subscription is unavailable or the carrier cannot be identified.
+ */
+ public int getCarrierIdFromSimMccMnc() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCarrierIdFromMccMnc(getSlotIndex(), getSimOperator(), true);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
+ /**
+ * Returns carrier id based on MCCMNC (returned by {@link #getSimOperator()}) only. This is
+ * used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()}
+ * are not found.
+ *
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
+ * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
+ * by default. After carrier id table update, a new carrier id was assigned. If apps don't
+ * take the update with the new id, it might be helpful to always fallback by using carrier
+ * id based on MCCMNC if there is no match.
+ *
+ * @return matching carrier id from passing MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
+ * subscription is unavailable or the carrier cannot be identified.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getCarrierIdFromMccMnc(String mccmnc) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc, false);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
+ /**
* Return a list of certs in hex string from loaded carrier privileges access rules.
*
* @return a list of certificate in hex string. return {@code null} if there is no certs
@@ -8540,48 +8705,6 @@
}
/**
- * Returns MNO carrier id of the current subscription’s MCCMNC.
- * <p>MNO carrier id can be solely identified by subscription mccmnc. This is mainly used
- * for MNO fallback when exact carrier id {@link #getSimCarrierId()}
- * configurations are not found.
- *
- * @return MNO carrier id of the current subscription. Return the value same as carrier id
- * {@link #getSimCarrierId()}, if MNO carrier id cannot be identified.
- * @hide
- */
- public int getSimMNOCarrierId() {
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.getSubscriptionMNOCarrierId(getSubId());
- }
- } catch (RemoteException ex) {
- // This could happen if binder process crashes.
- }
- return UNKNOWN_CARRIER_ID;
- }
-
- /**
- * Returns carrier id based on MCCMNC only. This is for fallback when exact carrier id
- * {@link #getSimCarrierId()} configurations are not found
- *
- * @return matching carrier id from passing mccmnc.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public int getCarrierIdFromMccMnc(String mccmnc) {
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc);
- }
- } catch (RemoteException ex) {
- // This could happen if binder process crashes.
- }
- return UNKNOWN_CARRIER_ID;
- }
-
- /**
* Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
* All uicc applications are uniquely identified by application ID, represented by the hex
* string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220
@@ -9374,8 +9497,13 @@
/**
* Get the emergency number list based on current locale, sim, default, modem and network.
*
- * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
- * the smaller index in the returned list.
+ * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+ * priority sources will be located at the smaller index; the priority order of sources are:
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
*
* <p>The subscriptions which the returned list would be based on, are all the active
* subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9384,8 +9512,9 @@
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return Map including the key as the active subscription ID (Note: if there is no active
- * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
- * as the list of {@link EmergencyNumber}; null if this information is not available.
+ * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+ * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+ * a SecurityException if the caller does not have the permission.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@Nullable
@@ -9406,8 +9535,13 @@
* Get the per-category emergency number list based on current locale, sim, default, modem
* and network.
*
- * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
- * the smaller index in the returned list.
+ * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+ * priority sources will be located at the smaller index; the priority order of sources are:
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
*
* <p>The subscriptions which the returned list would be based on, are all the active
* subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9428,8 +9562,9 @@
* <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
* </ol>
* @return Map including the key as the active subscription ID (Note: if there is no active
- * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
- * as the list of {@link EmergencyNumber}; null if this information is not available.
+ * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+ * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+ * a SecurityException if the caller does not have the permission.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@Nullable
@@ -9476,7 +9611,44 @@
if (telephony == null) {
return false;
}
- return telephony.isCurrentEmergencyNumber(number);
+ return telephony.isCurrentEmergencyNumber(number, true);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the supplied number is an emergency number based on current locale, sim, default,
+ * modem and network.
+ *
+ * <p> Specifically, this method will return {@code true} if the specified number is an
+ * emergency number, *or* if the number simply starts with the same digits as any current
+ * emergency number.
+ *
+ * <p>The subscriptions which the identification would be based on, are all the active
+ * subscriptions, no matter which subscription could be used to create TelephonyManager.
+ *
+ * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+ * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param number - the number to look up
+ * @return {@code true} if the given number is an emergency number or it simply starts with
+ * the same digits of any current emergency number based on current locale, sim, modem and
+ * network; {@code false} if it is not; or throw an SecurityException if the caller does not
+ * have the required permission/privileges
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isCurrentPotentialEmergencyNumber(@NonNull String number) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return false;
+ }
+ return telephony.isCurrentEmergencyNumber(number, false);
} catch (RemoteException ex) {
Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
}
@@ -9533,4 +9705,34 @@
}
return subId;
}
+
+ /**
+ * Update availability of a list of networks in the current location.
+ *
+ * This api should be called to inform AlternativeNetwork Service about the availability
+ * of a network at the current location. This information will be used by AlternativeNetwork
+ * service to decide to attach to the network opportunistically. If an empty list is passed,
+ * it is assumed that no network is available.
+ * Requires that the calling app has carrier privileges on both primary and
+ * secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ * @param availableNetworks is a list of available network information.
+ * @return true if request is accepted
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ public boolean updateAvailableNetworks(List<AvailableNetworkInfo> availableNetworks) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ boolean ret = false;
+ try {
+ IAns iAlternativeNetworkService = getIAns();
+ if (iAlternativeNetworkService != null) {
+ ret = iAlternativeNetworkService.updateAvailableNetworks(availableNetworks,
+ pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex);
+ }
+ return ret;
+ }
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 4fd7066..8d148c3 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -693,7 +693,7 @@
mmsc, mmsProxyAddress, mmsProxyPort, user, password, authType, mApnTypeBitmask,
protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, profileId,
modemCognitive, maxConns, waitTime, maxConnsTime, mtu, mvnoType, mvnoMatchData,
- Carriers.NO_SET_SET, TelephonyManager.UNKNOWN_CARRIER_ID);
+ Carriers.NO_APN_SET_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
}
/**
@@ -740,11 +740,11 @@
networkTypeBitmask,
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.MODEM_COGNITIVE)) == 1,
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME)),
+ Telephony.Carriers.MODEM_PERSIST)) == 1,
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNECTIONS)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME_RETRY)),
cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.MAX_CONNS_TIME)),
+ Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
getMvnoTypeIntFromString(
cursor.getString(cursor.getColumnIndexOrThrow(
@@ -870,7 +870,7 @@
int mtu = UNSET_MTU;
String mvnoType = "";
String mvnoMatchData = "";
- int apnSetId = Carriers.NO_SET_SET;
+ int apnSetId = Carriers.NO_APN_SET_ID;
int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
if (version == 1) {
typeArray = new String[a.length - 13];
@@ -969,7 +969,7 @@
*/
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("[ApnSettingV5] ")
+ sb.append("[ApnSettingV6] ")
.append(mEntryName)
.append(", ").append(mId)
.append(", ").append(mOperatorNumeric)
@@ -996,6 +996,7 @@
sb.append(", ").append(mPermanentFailed);
sb.append(", ").append(mNetworkTypeBitmask);
sb.append(", ").append(mApnSetId);
+ sb.append(", ").append(mCarrierId);
return sb.toString();
}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 4af31b5..bef1142 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -125,7 +125,6 @@
*
* @param result The result code. Must be one of the {@link ResultCode}.
*/
- @SystemApi
public void onSetDataProfileComplete(@ResultCode int result) {
IDataServiceCallback callback = mCallback.get();
if (callback != null) {
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 41f7bd7..fe062d5 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -22,6 +22,7 @@
import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -150,6 +151,7 @@
@IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EMERGENCY_NUMBER_SOURCE_SIM,
+ EMERGENCY_NUMBER_SOURCE_DATABASE,
EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
EMERGENCY_NUMBER_SOURCE_DEFAULT
})
@@ -169,6 +171,10 @@
* Reference: 3gpp 22.101, Section 10 - Emergency Calls
*/
public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
+ /**
+ * Bit-field which indicates the number is from the platform-maintained database.
+ */
+ public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 1 << 4;
/** Bit-field which indicates the number is from the modem config. */
public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
EmergencyNumberSource.MODEM_CONFIG;
@@ -187,21 +193,24 @@
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+ EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE);
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
}
private final String mNumber;
private final String mCountryIso;
+ private final String mMnc;
private final int mEmergencyServiceCategoryBitmask;
private final int mEmergencyNumberSourceBitmask;
/** @hide */
public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
- int emergencyServiceCategories,
+ @NonNull String mnc, int emergencyServiceCategories,
int emergencyNumberSources) {
this.mNumber = number;
this.mCountryIso = countryIso;
+ this.mMnc = mnc;
this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
}
@@ -210,6 +219,7 @@
public EmergencyNumber(Parcel source) {
mNumber = source.readString();
mCountryIso = source.readString();
+ mMnc = source.readString();
mEmergencyServiceCategoryBitmask = source.readInt();
mEmergencyNumberSourceBitmask = source.readInt();
}
@@ -236,6 +246,15 @@
}
/**
+ * Get the Mobile Network Code of the emergency number.
+ *
+ * @return the Mobile Network Code of the emergency number.
+ */
+ public String getMnc() {
+ return mMnc;
+ }
+
+ /**
* Returns the bitmask of emergency service categories of the emergency number.
*
* @return bitmask of the emergency service categories
@@ -338,6 +357,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mNumber);
dest.writeString(mCountryIso);
+ dest.writeString(mMnc);
dest.writeInt(mEmergencyServiceCategoryBitmask);
dest.writeInt(mEmergencyNumberSourceBitmask);
}
@@ -350,10 +370,10 @@
@Override
public String toString() {
- return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso
- + " / [ServiceCategories]"
- + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
- + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+ return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
+ + "|Mnc-" + mMnc
+ + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
+ + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
}
@Override
@@ -373,6 +393,7 @@
* The priority of sources are defined as follows:
* EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
* EMERGENCY_NUMBER_SOURCE_SIM >
+ * EMERGENCY_NUMBER_SOURCE_DATABASE >
* EMERGENCY_NUMBER_SOURCE_DEFAULT >
* EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
*
@@ -385,7 +406,9 @@
if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
score += 1 << 3;
}
- // TODO add a score if the number comes from Google's emergency number database
+ if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+ score += 1 << 2;
+ }
if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
score += 1 << 1;
}
@@ -412,14 +435,104 @@
< emergencyNumber.getDisplayPriorityScore()) {
return 1;
} else {
- /**
- * TODO if both numbers have the same display priority score, the number matches the
- * Google's emergency number database has a higher display priority.
- */
return 0;
}
}
+ /**
+ * In-place merge same emergency numbers in the emergency number list.
+ *
+ * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+ * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+ * for the same EmergencyNumber.
+ *
+ * @param emergencyNumberList the emergency number list to process
+ *
+ * @hide
+ */
+ public static void mergeSameNumbersInEmergencyNumberList(
+ List<EmergencyNumber> emergencyNumberList) {
+ if (emergencyNumberList == null) {
+ return;
+ }
+ Set<EmergencyNumber> mergedEmergencyNumber = new HashSet<>();
+ for (int i = 0; i < emergencyNumberList.size(); i++) {
+ // Skip the check because it was merged.
+ if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+ continue;
+ }
+ for (int j = i + 1; j < emergencyNumberList.size(); j++) {
+ if (isSameEmergencyNumber(
+ emergencyNumberList.get(i), emergencyNumberList.get(j))) {
+ Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: "
+ + emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j));
+ // Set the merged emergency number in the current position
+ emergencyNumberList.set(i, mergeNumbers(
+ emergencyNumberList.get(i), emergencyNumberList.get(j)));
+ // Mark the emergency number has been merged
+ mergedEmergencyNumber.add(emergencyNumberList.get(j));
+ }
+ }
+ }
+ // Remove the marked emergency number in the orignal list
+ for (int i = 0; i < emergencyNumberList.size(); i++) {
+ if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+ emergencyNumberList.remove(i--);
+ }
+ }
+ }
+
+ /**
+ * Check if two emergency numbers are the same.
+ *
+ * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+ * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+ * for the same EmergencyNumber.
+ *
+ * @param first first EmergencyNumber to compare
+ * @param second second EmergencyNumber to compare
+ * @return true if they are the same EmergencyNumbers; false otherwise.
+ *
+ * @hide
+ */
+ public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
+ if (!first.getNumber().equals(second.getNumber())) {
+ return false;
+ }
+ if (!first.getCountryIso().equals(second.getCountryIso())) {
+ return false;
+ }
+ if (!first.getMnc().equals(second.getMnc())) {
+ return false;
+ }
+ if (first.getEmergencyServiceCategoryBitmask()
+ != second.getEmergencyServiceCategoryBitmask()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get a merged EmergencyNumber for two numbers if they are the same.
+ *
+ * @param first first EmergencyNumber to compare
+ * @param second second EmergencyNumber to compare
+ * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
+ *
+ * @hide
+ */
+ public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
+ if (isSameEmergencyNumber(first, second)) {
+ return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
+ first.getEmergencyServiceCategoryBitmask(),
+ first.getEmergencyNumberSourceBitmask()
+ | second.getEmergencyNumberSourceBitmask());
+ }
+ return null;
+ }
+
public static final Parcelable.Creator<EmergencyNumber> CREATOR =
new Parcelable.Creator<EmergencyNumber>() {
@Override
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 3b1ef3f..994c49c 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -155,7 +155,7 @@
* Requests all the profiles on eUicc.
*
* @param cardId The Id of the eUICC.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and all the profiles.
*/
public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor,
@@ -179,7 +179,7 @@
*
* @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and profile.
*/
public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
@@ -204,7 +204,7 @@
* @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
* @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
*/
public void disableProfile(String cardId, String iccid, boolean refresh,
@@ -230,7 +230,7 @@
* @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile to switch to.
* @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and the EuiccProfileInfo enabled.
*/
public void switchToProfile(String cardId, String iccid, boolean refresh,
@@ -255,7 +255,7 @@
* @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
* @param nickname The nickname of the profile.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
*/
public void setNickname(String cardId, String iccid, String nickname,
@@ -279,7 +279,7 @@
*
* @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
*/
public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
@@ -304,7 +304,7 @@
* @param cardId The Id of the eUICC.
* @param options Bits of the options of resetting which parts of the eUICC memory. See
* EuiccCard for details.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
*/
public void resetMemory(String cardId, @ResetOption int options,
@@ -327,7 +327,7 @@
* Requests the default SM-DP+ address from eUICC.
*
* @param cardId The Id of the eUICC.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and the default SM-DP+ address.
*/
public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor,
@@ -350,7 +350,7 @@
* Requests the SM-DS address from eUICC.
*
* @param cardId The Id of the eUICC.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and the SM-DS address.
*/
public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor,
@@ -374,7 +374,7 @@
*
* @param cardId The Id of the eUICC.
* @param defaultSmdpAddress The default SM-DP+ address to set.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
*/
public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress,
@@ -398,7 +398,7 @@
* Requests Rules Authorisation Table.
*
* @param cardId The Id of the eUICC.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and the rule authorisation table.
*/
public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor,
@@ -421,7 +421,7 @@
* Requests the eUICC challenge for new profile downloading.
*
* @param cardId The Id of the eUICC.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and the challenge.
*/
public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor,
@@ -444,7 +444,7 @@
* Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
*
* @param cardId The Id of the eUICC.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and the info1.
*/
public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor,
@@ -467,7 +467,7 @@
* Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
*
* @param cardId The Id of the eUICC.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and the info2.
*/
public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor,
@@ -500,7 +500,7 @@
* GSMA RSP v2.0+.
* @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
* SM-DP+ server.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and a byte array which represents a
* {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
*/
@@ -540,7 +540,7 @@
* SM-DP+ server.
* @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
* by SM-DP+ server.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and a byte array which represents a
* {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
*/
@@ -572,7 +572,7 @@
*
* @param cardId The Id of the eUICC.
* @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and a byte array which represents a
* {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
*/
@@ -601,7 +601,7 @@
* @param cardId The Id of the eUICC.
* @param transactionId the transaction ID returned by SM-DP+ server.
* @param reason the cancel reason.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and an byte[] which represents a
* {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
*/
@@ -630,7 +630,7 @@
*
* @param cardId The Id of the eUICC.
* @param events bits of the event types ({@link EuiccNotification.Event}) to list.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and the list of notifications.
*/
public void listNotifications(String cardId, @EuiccNotification.Event int events,
@@ -654,7 +654,7 @@
*
* @param cardId The Id of the eUICC.
* @param events bits of the event types ({@link EuiccNotification.Event}) to list.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and the list of notifications.
*/
public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
@@ -678,7 +678,7 @@
*
* @param cardId The Id of the eUICC.
* @param seqNumber the sequence number of the notification.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code and the notification.
*/
public void retrieveNotification(String cardId, int seqNumber,
@@ -702,7 +702,7 @@
*
* @param cardId The Id of the eUICC.
* @param seqNumber the sequence number of the notification.
- * @param executor The executor through which the callback should be invoke.
+ * @param executor The executor through which the callback should be invoked.
* @param callback the callback to get the result code.
*/
public void removeNotificationFromList(String cardId, int seqNumber,
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 8fcdb6e..f124595 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -16,11 +16,15 @@
package android.telephony.ims;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class enables an application to get details on why a method call failed.
*
@@ -30,158 +34,292 @@
public final class ImsReasonInfo implements Parcelable {
/**
- * Specific code of each types
+ * The Reason is unspecified.
*/
public static final int CODE_UNSPECIFIED = 0;
- /**
- * LOCAL
- */
+
+ // LOCAL
+
// IMS -> Telephony
- // The passed argument is an invalid
+ /**
+ * The passed argument is invalid.
+ */
public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
- // The operation is invoked in invalid call state
+ /**
+ * The operation was invoked while in an invalid call state.
+ */
public static final int CODE_LOCAL_ILLEGAL_STATE = 102;
- // IMS service internal error
+ /**
+ * IMS service internal error
+ */
public static final int CODE_LOCAL_INTERNAL_ERROR = 103;
- // IMS service goes down (service connection is lost)
+ /**
+ * ImsService has crashed (service connection is lost).
+ */
public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106;
- // No pending incoming call exists
+ /**
+ * No pending incoming call exists
+ */
public static final int CODE_LOCAL_NO_PENDING_CALL = 107;
- // IMS Call ended during conference merge process
+ /**
+ * IMS Call ended during conference merge process
+ */
public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108;
// IMS -> Telephony
- // Service unavailable; by power off
+ /**
+ * Service unavailable; radio power off
+ */
public static final int CODE_LOCAL_POWER_OFF = 111;
- // Service unavailable; by low battery
+ /**
+ * Service unavailable; low battery
+ */
public static final int CODE_LOCAL_LOW_BATTERY = 112;
- // Service unavailable; by out of service (data service state)
+ /**
+ * Service unavailable; out of service (data service state)
+ */
public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
- // Service unavailable; by no LTE coverage
- // (VoLTE is not supported even though IMS is registered)
+ /**
+ * Service unavailable; no LTE coverage
+ * (VoLTE is not supported even though IMS is registered)
+ */
public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122;
- // Service unavailable; by located in roaming area
+ /**
+ * Service unavailable; located in roaming area
+ */
public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
- // Service unavailable; by IP changed
+ /**
+ * Service unavailable; IP changed
+ */
public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
- // Service unavailable; other
+ /**
+ * Service unavailable; for an unspecified reason
+ */
public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
- // Service unavailable; IMS connection is lost (IMS is not registered)
+ /**
+ * Service unavailable; IMS is not registered
+ */
public static final int CODE_LOCAL_NOT_REGISTERED = 132;
// IMS <-> Telephony
- // Max call exceeded
+ /**
+ * Maximum number of simultaneous calls exceeded
+ */
public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
// IMS <- Telephony
- // Call busy
+ /**
+ * The call is busy.
+ */
public static final int CODE_LOCAL_CALL_BUSY = 142;
- // Call decline
+ /**
+ * The Call has been declined locally on this device.
+ */
public static final int CODE_LOCAL_CALL_DECLINE = 143;
// IMS -> Telephony
- // SRVCC is in progress
+ /**
+ * Can not complete call; an SRVCC is in progress.
+ */
public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
- // Resource reservation is failed (QoS precondition)
+ /**
+ * Can not complete call; resource reservation is failed (QoS precondition)
+ */
public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
- // Retry CS call; VoLTE service can't be provided by the network or remote end
- // Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ /**
+ * VoLTE service can't be provided by the network or remote end, retry the call.
+ * Resolve the extra code provided in (EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ */
public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
- // Retry VoLTE call; VoLTE service can't be provided by the network temporarily
+ /**
+ * VoLTE service can't be provided by the network temporarily, retry the call.
+ */
public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147;
- // IMS call is already terminated (in TERMINATED state)
+ /**
+ * IMS call is already terminated (in TERMINATED state).
+ */
public static final int CODE_LOCAL_CALL_TERMINATED = 148;
- // Handover not feasible
+ /**
+ * Call was disconnected because a handover is not feasible due to network conditions.
+ */
public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
- /**
+ /*
* TIMEOUT (IMS -> Telephony)
*/
- // 1xx waiting timer is expired after sending INVITE request (MO only)
+ /**
+ * 1xx waiting timer is expired after sending INVITE request (MO calls only)
+ */
public static final int CODE_TIMEOUT_1XX_WAITING = 201;
- // User no answer during call setup operation (MO/MT)
- // MO : 200 OK to INVITE request is not received,
- // MT : No action from user after alerting the call
+ /**
+ * User didn't answer during call setup operation (MO/MT)
+ * MO : 200 OK to INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
public static final int CODE_TIMEOUT_NO_ANSWER = 202;
- // User no answer during call update operation (MO/MT)
- // MO : 200 OK to re-INVITE request is not received,
- // MT : No action from user after alerting the call
+ /**
+ * User no answer during call update operation (MO/MT)
+ * MO : 200 OK to re-INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
- //Call was blocked by call barring
+ /**
+ * The call was blocked by call barring configuration.
+ */
public static final int CODE_CALL_BARRED = 240;
- //Call failures for FDN
+ /**
+ * The operation is restricted to fixed dialing numbers only.
+ */
public static final int CODE_FDN_BLOCKED = 241;
- // Network does not accept the emergency call request because IMEI was used as identification
- // and this capability is not supported by the network.
+ /**
+ * Network rejected the emergency call request because IMEI was used as identification
+ * and this capability is not supported by the network.
+ */
public static final int CODE_IMEI_NOT_ACCEPTED = 243;
//STK CC errors
+ /**
+ * Stk Call Control modified DIAL request to USSD request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+ /**
+ * Stk Call Control modified DIAL request to SS request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+ /**
+ * Stk Call Control modified DIAL request to DIAL with modified data.
+ */
public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+ /**
+ * Stk Call Control modified DIAL request to Video DIAL request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+ /**
+ * Stk Call Control modified Video DIAL request to DIAL request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+ /**
+ * Stk Call Control modified Video DIAL request to Video DIAL request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+ /**
+ * Stk Call Control modified Video DIAL request to SS request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+ /**
+ * Stk Call Control modified Video DIAL request to USSD request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
- /**
+ /*
* STATUSCODE (SIP response code) (IMS -> Telephony)
*/
// 3xx responses
- // SIP request is redirected
+ /**
+ * SIP 3xx response: SIP request is redirected
+ */
public static final int CODE_SIP_REDIRECTED = 321;
// 4xx responses
- // 400 : Bad Request
+ /**
+ * Sip 400 response : Bad Request
+ */
public static final int CODE_SIP_BAD_REQUEST = 331;
- // 403 : Forbidden
+ /**
+ * Sip 403 response : Forbidden
+ */
public static final int CODE_SIP_FORBIDDEN = 332;
- // 404 : Not Found
+ /**
+ * Sip 404 response : Not Found
+ */
public static final int CODE_SIP_NOT_FOUND = 333;
- // 415 : Unsupported Media Type
- // 416 : Unsupported URI Scheme
- // 420 : Bad Extension
+ /**
+ * Not supported, because of one of the following:
+ * SIP response 415 : Unsupported Media Type,
+ * SIP response 416 : Unsupported URI Scheme,
+ * SIP response 420 : Bad Extension
+ */
public static final int CODE_SIP_NOT_SUPPORTED = 334;
- // 408 : Request Timeout
+ /**
+ * SIP response 408 : Request Timeout.
+ */
public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
- // 480 : Temporarily Unavailable
+ /**
+ * SIP response 480 : Temporarily Unavailable
+ */
public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
- // 484 : Address Incomplete
+ /**
+ * SIP response 484 : Address Incomplete
+ */
public static final int CODE_SIP_BAD_ADDRESS = 337;
- // 486 : Busy Here
- // 600 : Busy Everywhere
+ /**
+ * Returned a busy response, may be one of the following:
+ * SIP response 486 : Busy Here,
+ * SIP response 600 : Busy Everywhere
+ */
public static final int CODE_SIP_BUSY = 338;
- // 487 : Request Terminated
+ /**
+ * SIP response 487 : Request Terminated
+ */
public static final int CODE_SIP_REQUEST_CANCELLED = 339;
- // 406 : Not Acceptable
- // 488 : Not Acceptable Here
- // 606 : Not Acceptable
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 406 : Not Acceptable
+ * SIP response 488 : Not Acceptable Here
+ * SIP response 606 : Not Acceptable
+ */
public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
- // 410 : Gone
- // 604 : Does Not Exist Anywhere
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 410 : Gone
+ * SIP response 604 : Does Not Exist Anywhere
+ */
public static final int CODE_SIP_NOT_REACHABLE = 341;
- // Others
+ /**
+ * Received another unspecified error SIP response from the client.
+ */
public static final int CODE_SIP_CLIENT_ERROR = 342;
- // 481 Transaction Does Not Exist
+ /**
+ * SIP response 481: Transaction Does Not Exist
+ */
public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343;
// 5xx responses
- // 501 : Server Internal Error
+ /**
+ * SIP response 501 : Server Internal Error
+ */
public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
- // 503 : Service Unavailable
+ /**
+ * SIP response 503 : Service Unavailable
+ */
public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
- // 504 : Server Time-out
+ /**
+ * SIP response 504 : Server Time-out
+ */
public static final int CODE_SIP_SERVER_TIMEOUT = 353;
- // Others
+ /**
+ * Received an unspecified SIP server error response.
+ */
public static final int CODE_SIP_SERVER_ERROR = 354;
// 6xx responses
- // 603 : Decline
+ /**
+ * 603 : Decline
+ */
public static final int CODE_SIP_USER_REJECTED = 361;
- // Others
+ /**
+ * Unspecified 6xx error.
+ */
public static final int CODE_SIP_GLOBAL_ERROR = 362;
- // Emergency failure
+
+ /**
+ * Emergency call failed in the modem with a temporary fail cause and should be redialed on this
+ * slot.
+ */
public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+ /**
+ * Emergency call failed in the modem with a permanent fail cause and should not be redialed on
+ * this slot. If there are any other slots available for emergency calling, try those.
+ */
public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
/**
@@ -193,82 +331,149 @@
*/
public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
- /**
+ /*
* MEDIA (IMS -> Telephony)
*/
- // Media resource initialization failed
+ /**
+ * Media resource initialization failed
+ */
public static final int CODE_MEDIA_INIT_FAILED = 401;
- // RTP timeout (no audio / video traffic in the session)
+ /**
+ * RTP timeout (no audio / video traffic in the session)
+ */
public static final int CODE_MEDIA_NO_DATA = 402;
- // Media is not supported; so dropped the call
+ /**
+ * Media is not supported; so dropped the call
+ */
public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403;
- // Unknown media related errors
+ /**
+ * Unspecified media related error.
+ */
public static final int CODE_MEDIA_UNSPECIFIED = 404;
- /**
+ /*
* USER
*/
// Telephony -> IMS
- // User triggers the call end
+ /**
+ * User triggers the call to be terminated.
+ */
public static final int CODE_USER_TERMINATED = 501;
- // No action while an incoming call is ringing
+ /**
+ * No action was taken while an incoming call was ringing.
+ */
public static final int CODE_USER_NOANSWER = 502;
- // User ignores an incoming call
+ /**
+ * User ignored an incoming call.
+ */
public static final int CODE_USER_IGNORE = 503;
- // User declines an incoming call
+ /**
+ * User declined an incoming call.
+ */
public static final int CODE_USER_DECLINE = 504;
- // Device declines/ends a call due to low battery
+ /**
+ * Device declined/ended a call due to a low battery condition.
+ */
public static final int CODE_LOW_BATTERY = 505;
- // Device declines call due to blacklisted call ID
+ /**
+ * Device declined a call due to a blacklisted caller ID.
+ */
public static final int CODE_BLACKLISTED_CALL_ID = 506;
// IMS -> Telephony
- // The call is terminated by the network or remote user
+ /**
+ * The call has been terminated by the network or remote user.
+ */
public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
- /**
- * Extra codes for the specific code value
- * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
- */
- // Try to connect CS call; normal
- public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
- // Try to connect CS call without the notification to user
- public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
- // Try to connect CS call by the settings of the menu
- public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
-
- /**
+ /*
* UT
*/
+ /**
+ * UT is currently not supported on this device.
+ */
public static final int CODE_UT_NOT_SUPPORTED = 801;
+ /**
+ * UT services are currently not available on this device.
+ */
public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+ /**
+ * The requested UT operation is not allowed.
+ */
public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+ /**
+ * The UT request resulted in a network error.
+ */
public static final int CODE_UT_NETWORK_ERROR = 804;
+ /**
+ * The password entered for UT operations does not match the stored password.
+ */
public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
//STK CC errors
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a dial command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a USSD command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to another supplementary service command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a video call dial command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
+ /**@hide*/
+ @IntDef(value = {
+ CODE_UT_NOT_SUPPORTED,
+ CODE_UT_SERVICE_UNAVAILABLE,
+ CODE_UT_OPERATION_NOT_ALLOWED,
+ CODE_UT_NETWORK_ERROR,
+ CODE_UT_CB_PASSWORD_MISMATCH,
+ CODE_UT_SS_MODIFIED_TO_DIAL,
+ CODE_UT_SS_MODIFIED_TO_USSD,
+ CODE_UT_SS_MODIFIED_TO_SS,
+ CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO
+ }, prefix = "CODE_UT_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UtReason {}
+
/**
- * ECBM
+ * Emergency callback mode is not supported.
*/
public static final int CODE_ECBM_NOT_SUPPORTED = 901;
/**
- * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+ * Fail code used to indicate that Multi-endpoint is not supported by the IMS framework.
*/
public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902;
/**
- * Ims Registration error code
+ * IMS Registration error code
*/
public static final int CODE_REGISTRATION_ERROR = 1000;
- /**
+ /*
* CALL DROP error codes (Call could drop because of many reasons like Network not available,
* handover, failed, etc)
*/
+ /**
+ * MT call has ended due to a release from the network because the call was answered elsewhere.
+ */
+ public static final int CODE_ANSWERED_ELSEWHERE = 1014;
+
+ /**
+ * For MultiEndpoint - Call Pull request has failed.
+ */
+ public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
+
+ /**
+ * For MultiEndpoint - Call has been pulled from primary to secondary.
+ */
+ public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
/**
* CALL DROP error code for the case when a device is ePDG capable and when the user is on an
@@ -279,46 +484,35 @@
public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
/**
- * MT call has ended due to a release from the network
- * because the call was answered elsewhere
- */
- public static final int CODE_ANSWERED_ELSEWHERE = 1014;
-
- /**
- * For MultiEndpoint - Call Pull request has failed
- */
- public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
-
- /**
- * For MultiEndpoint - Call has been pulled from primary to secondary
- */
- public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
-
- /**
- * Supplementary services (HOLD/RESUME) failure error codes.
- * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ * Supplementary Services (HOLD/RESUME) - the command failed.
*/
public static final int CODE_SUPP_SVC_FAILED = 1201;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command was cancelled.
+ */
public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command resulted in a re-invite collision.
+ */
public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203;
/**
- * DPD Procedure received no response or send failed
+ * DPD Procedure received no response or send failed.
*/
public static final int CODE_IWLAN_DPD_FAILURE = 1300;
/**
- * Establishment of the ePDG Tunnel Failed
+ * Establishment of the ePDG Tunnel Failed.
*/
public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400;
/**
- * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+ * Re-keying of the ePDG Tunnel Failed; may not always result in teardown.
*/
public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401;
/**
- * Connection to the packet gateway is lost
+ * Connection to the packet gateway is lost.
*/
public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402;
@@ -571,8 +765,10 @@
*/
public static final int CODE_REJECT_ONGOING_CS_CALL = 1621;
- /* OEM specific error codes. To be used by OEMs when they don't want to
- reveal error code which would be replaced by ERROR_UNSPECIFIED */
+ /*
+ * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
+ * would be replaced by ERROR_UNSPECIFIED.
+ */
public static final int CODE_OEM_CAUSE_1 = 0xf001;
public static final int CODE_OEM_CAUSE_2 = 0xf002;
public static final int CODE_OEM_CAUSE_3 = 0xf003;
@@ -597,6 +793,33 @@
= "Forbidden. Not Authorized for Service";
+ /*
+ * Extra codes for the specific code value
+ * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+ */
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS and do not notify the user.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS by using the settings.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+
// For main reason code
/** @hide */
@UnsupportedAppUsage
@@ -638,29 +861,28 @@
}
/**
- *
+ * @return an integer representing more information about the completion of an operation.
*/
public int getCode() {
return mCode;
}
/**
- *
+ * @return an optional OEM specified code that provides extra information.
*/
public int getExtraCode() {
return mExtraCode;
}
/**
- *
+ * @return an optional OEM specified string that provides extra information about the operation
+ * result.
*/
public String getExtraMessage() {
return mExtraMessage;
}
/**
- * Returns the string format of {@link ImsReasonInfo}
- *
* @return the string format of {@link ImsReasonInfo}
*/
public String toString() {
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index db5ba47..3a82517 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -65,6 +65,17 @@
public static final int SS_INCOMING_BARRING_DN = 21;
public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
+
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ACTIVATION,
+ SS_DEACTIVATION,
+ SS_INTERROGATION,
+ SS_REGISTRATION,
+ SS_ERASURE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType{}
+
//Supplementary Service Request Types
public static final int SS_ACTIVATION = 0;
public static final int SS_DEACTIVATION = 1;
@@ -72,6 +83,17 @@
public static final int SS_REGISTRATION = 3;
public static final int SS_ERASURE = 4;
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ALL_TELE_AND_BEARER_SERVICES,
+ SS_ALL_TELESEVICES,
+ SS_TELEPHONY,
+ SS_ALL_DATA_TELESERVICES,
+ SS_SMS_SERVICES,
+ SS_ALL_TELESERVICES_EXCEPT_SMS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TeleserviceType{}
+
// Supplementary Service Teleservice Type
public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
public static final int SS_ALL_TELESEVICES = 1;
@@ -191,21 +213,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceType{}
- /** @hide */
- @IntDef(flag = true, prefix = { "SERVICE_CLASS" }, value = {
- SERVICE_CLASS_NONE,
- SERVICE_CLASS_VOICE,
- SERVICE_CLASS_DATA,
- SERVICE_CLASS_FAX,
- SERVICE_CLASS_SMS,
- SERVICE_CLASS_DATA_CIRCUIT_SYNC,
- SERVICE_CLASS_DATA_CIRCUIT_ASYNC,
- SERVICE_CLASS_DATA_PACKET_ACCESS,
- SERVICE_CLASS_DATA_PAD
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ServiceClass{}
-
/**
* The Service type of this Supplementary service.
* @hide
@@ -221,7 +228,7 @@
* {@link #SS_ERASURE}
* @hide
*/
- public final int requestType;
+ public final @RequestType int requestType;
/**
* Supplementary Service teleservice type:
@@ -234,14 +241,14 @@
*
* @hide
*/
- public final int teleserviceType;
+ public final @TeleserviceType int teleserviceType;
/**
* Supplementary Service service class.
*
* @hide
*/
- public final @ServiceClass int serviceClass;
+ public final @ServiceClassFlags int serviceClass;
/**
* Result of Supplementary Service operation. Valid values are:
@@ -285,7 +292,7 @@
* @see #build()
*/
public Builder(@ServiceType int serviceType, int requestType, int teleserviceType,
- @ServiceClass int serviceClass, int result) {
+ @ServiceClassFlags int serviceClass, int result) {
mImsSsData = new ImsSsData(serviceType, requestType, teleserviceType, serviceClass,
result);
}
@@ -294,7 +301,7 @@
* Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
* data.
*/
- public Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
+ public @NonNull Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
mImsSsData.mImsSsInfo = imsSsInfos;
return this;
}
@@ -303,7 +310,8 @@
* Set the array of {@link ImsCallForwardInfo}s that are associated with this supplementary
* service data.
*/
- public Builder setCallForwardingInfo(@NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
+ public @NonNull Builder setCallForwardingInfo(
+ @NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
mImsSsData.mCfInfo = imsCallForwardInfos;
return this;
}
@@ -311,7 +319,7 @@
/**
* @return an {@link ImsSsData} containing optional parameters.
*/
- public ImsSsData build() {
+ public @NonNull ImsSsData build() {
return mImsSsData;
}
}
@@ -337,7 +345,7 @@
* success, or ImsReasonInfo code if the result is a failure.
*/
public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType,
- @ServiceClass int serviceClass, int result) {
+ @ServiceClassFlags int serviceClass, int result) {
this.serviceType = serviceType;
this.requestType = requestType;
this.teleserviceType = teleserviceType;
@@ -449,14 +457,9 @@
}
/**
- * Supplementary Service request Type:
- * {@link #SS_ACTIVATION),
- * {@link #SS_DEACTIVATION},
- * {@link #SS_INTERROGATION},
- * {@link #SS_REGISTRATION},
- * {@link #SS_ERASURE}
+ * Supplementary Service request Type.
*/
- public int getRequestType() {
+ public @RequestType int getRequestType() {
return requestType;
}
@@ -468,31 +471,25 @@
}
/**
- * Supplementary Service teleservice type:
- * {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
- * {@link #SS_ALL_TELESEVICES},
- * {@link #SS_TELEPHONY},
- * {@link #SS_ALL_DATA_TELESERVICES},
- * {@link #SS_SMS_SERVICES},
- * {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+ * Supplementary Service teleservice type.
*/
- public int getTeleserviceType() {
+ public @TeleserviceType int getTeleserviceType() {
return teleserviceType;
}
/**
* Supplementary Service service class.
*/
- public @ServiceClass int getServiceClass() {
+ public @ServiceClassFlags int getServiceClass() {
return serviceClass;
}
/**
* Result of Supplementary Service operation. Valid values are:
* {@link #RESULT_SUCCESS} if the result is success, or
- * {@link ImsReasonInfo} CODE_* code if the result is a failure.
+ * {@link ImsReasonInfo.UtReason} code if the result is a failure.
*/
- public int getResult() {
+ public @ImsReasonInfo.UtReason int getResult() {
return result;
}
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 0af6e62..031f9e1 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -82,6 +82,7 @@
*/
public static final int SERVICE_PROVISIONED = 1;
+ /**@hide*/
@IntDef(value = {
CLIR_OUTGOING_DEFAULT,
CLIR_OUTGOING_INVOCATION,
@@ -141,6 +142,7 @@
*/
public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4;
+ /**@hide*/
@IntDef(value = {
CLIR_STATUS_NOT_PROVISIONED,
CLIR_STATUS_PROVISIONED_PERMANENT,
@@ -184,7 +186,7 @@
* Set the ICB number for IMS call barring.
* @param number The number in E.164 international format.
*/
- public Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
+ public @NonNull Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
mImsSsInfo.mIcbNum = number;
return this;
}
@@ -192,7 +194,7 @@
/**
* Set the provisioning status for a Supplementary Service interrogation response.
*/
- public Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
+ public @NonNull Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
mImsSsInfo.mProvisionStatus = provisionStatus;
return this;
}
@@ -201,7 +203,7 @@
* Set the Calling Line Identification Restriction (CLIR) status for a supplementary service
* interrogation response.
*/
- public Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
+ public @NonNull Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
mImsSsInfo.mClirInterrogationStatus = status;
return this;
}
@@ -209,7 +211,7 @@
/**
* Set the Calling line identification Restriction (CLIR) state for outgoing calls.
*/
- public Builder setClirOutgoingState(@ClirOutgoingState int state) {
+ public @NonNull Builder setClirOutgoingState(@ClirOutgoingState int state) {
mImsSsInfo.mClirOutgoingState = state;
return this;
}
@@ -217,7 +219,7 @@
/**
* @return a built {@link ImsSsInfo} containing optional the parameters that were set.
*/
- public ImsSsInfo build() {
+ public @NonNull ImsSsInfo build() {
return mImsSsInfo;
}
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 0abe45c..13539b8 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
@@ -32,15 +33,15 @@
import android.provider.ContactsContract.RawContacts;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
-import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import com.android.i18n.phonenumbers.NumberParseException;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import android.telephony.SubscriptionManager;
+import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import java.util.Locale;
@@ -112,6 +113,9 @@
public Uri contactRefUri;
public String lookupKey;
+ public ComponentName preferredPhoneAccountComponent;
+ public String preferredPhoneAccountId;
+
public long userType;
/**
@@ -264,6 +268,17 @@
info.contactDisplayPhotoUri = null;
}
+ columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ info.preferredPhoneAccountComponent =
+ ComponentName.unflattenFromString(cursor.getString(columnIndex));
+ }
+
+ columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_ID);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ info.preferredPhoneAccountId = cursor.getString(columnIndex);
+ }
+
// look for the custom ringtone, create from the string stored
// in the database.
// An empty string ("") in the database indicates a silent ringtone,
diff --git a/telephony/java/com/android/internal/telephony/IAns.aidl b/telephony/java/com/android/internal/telephony/IAns.aidl
index e9a4649..98bcd41 100755
--- a/telephony/java/com/android/internal/telephony/IAns.aidl
+++ b/telephony/java/com/android/internal/telephony/IAns.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.telephony.AvailableNetworkInfo;
interface IAns {
@@ -78,4 +79,23 @@
*
*/
int getPreferredData(String callingPackage);
+
+ /**
+ * Update availability of a list of networks in the current location.
+ *
+ * This api should be called if the caller is aware of the availability of a network
+ * at the current location. This information will be used by AlternativeNetwork service
+ * to decide to attach to the network. If an empty list is passed,
+ * it is assumed that no network is available.
+ * Requires that the calling app has carrier privileges on both primary and
+ * secondary subscriptions (see
+ * {@link #hasCarrierPrivileges}), or has permission
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ * @param availableNetworks is a list of available network information.
+ * @param callingPackage caller's package name
+ * @return true if request is accepted
+ *
+ */
+ boolean updateAvailableNetworks(in List<AvailableNetworkInfo> availableNetworks,
+ String callingPackage);
}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
similarity index 77%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
index c66a6cb..76918af 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package com.android.internal.telephony;
-parcelable ContentCaptureEvent;
+oneway interface INumberVerificationCallback {
+ void onCallReceived(String phoneNumber);
+ void onVerificationFailed(int reason);
+}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 79f0635..78fc0bc4 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -25,6 +25,7 @@
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.emergency.EmergencyNumber;
oneway interface IPhoneStateListener {
void onServiceStateChanged(in ServiceState serviceState);
@@ -53,5 +54,6 @@
void onPhoneCapabilityChanged(in PhoneCapability capability);
void onPreferredDataSubIdChanged(in int subId);
void onRadioPowerStateChanged(in int state);
+ void onEmergencyNumberListChanged(in Map emergencyNumberList);
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index bc44519..65d1a920 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -184,6 +184,15 @@
String setSubscriptionGroup(in int[] subIdList, String callingPackage);
/**
+ * Set whether a subscription is metered
+ *
+ * @param isMetered whether it’s a metered subscription.
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated
+ */
+ int setMetered(boolean isMetered, int subId);
+
+ /**
* Set which subscription is preferred for cellular data. It's
* designed to overwrite default data subscription temporarily.
*
@@ -201,6 +210,10 @@
*/
List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage);
+ boolean removeSubscriptionsFromGroup(in int[] subIdList, String callingPackage);
+
+ List<SubscriptionInfo> getSubscriptionsInGroup(int subId, String callingPackage);
+
int getSlotIndex(int subId);
int[] getSubId(int slotIndex);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 32e939a0..46366d6 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -35,6 +35,7 @@
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
+import android.telephony.PhoneNumberRange;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -49,6 +50,7 @@
import android.telephony.ims.aidl.IImsRegistrationCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
+import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.OperatorInfo;
import java.util.List;
@@ -871,6 +873,17 @@
String getCdmaMin(int subId);
/**
+ * Request that the next incoming call from a number matching {@code range} be intercepted.
+ * @param range The range of phone numbers the caller expects a phone call from.
+ * @param timeoutMillis The amount of time to wait for such a call, or
+ * {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser.
+ * @param callback the callback aidl
+ * @param callingPackage the calling package name.
+ */
+ void requestNumberVerification(in PhoneNumberRange range, long timeoutMillis,
+ in INumberVerificationCallback callback, String callingPackage);
+
+ /**
* Has the calling application been granted special privileges by the carrier.
*
* If any of the packages in the calling UID has carrier privileges, the
@@ -1324,18 +1337,6 @@
String getSubscriptionCarrierName(int subId);
/**
- * Returns MNO carrier id of the current subscription’s MCCMNC.
- * <p>MNO carrier id can be solely identified by subscription mccmnc. This is mainly used
- * for MNO fallback when exact carrier id {@link #getSimCarrierId()}
- * configurations are not found.
- *
- * @return MNO carrier id of the current subscription. Return the value same as carrier id
- * {@link #getSimCarrierId()}, if MNO carrier id cannot be identified.
- * @hide
- */
- int getSubscriptionMNOCarrierId(int subId);
-
- /**
* Returns fine-grained carrier id of the current subscription.
*
* <p>The precise carrier id can be used to further differentiate a carrier by different
@@ -1370,10 +1371,13 @@
* Returns carrier id based on MCCMNC only. This will return a MNO carrier id used for fallback
* check when exact carrier id {@link #getSimCarrierId()} configurations are not found
*
+ * @param isSubscriptionMccMnc. If {@true} it means this is a query for subscription mccmnc
+ * {@false} otherwise.
+ *
* @return carrier id from passing mccmnc.
* @hide
*/
- int getCarrierIdFromMccMnc(int slotIndex, String mccmnc);
+ int getCarrierIdFromMccMnc(int slotIndex, String mccmnc, boolean isSubscriptionMccMnc);
/**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns
@@ -1469,6 +1473,19 @@
SignalStrength getSignalStrength(int subId);
/**
+ * Get the card ID of the default eUICC card. If there is no eUICC, returns
+ * {@link #INVALID_CARD_ID}.
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @param subId subscription ID used for authentication
+ * @param callingPackage package making the call
+ * @return card ID of the default eUICC card.
+ * @hide
+ */
+ int getCardIdForDefaultEuicc(int subId, String callingPackage);
+
+ /**
* Get slot info for all the UICC slots.
* @return UiccSlotInfo array.
* @hide
@@ -1703,7 +1720,7 @@
/**
* Identify if the number is emergency number, based on all the active subscriptions.
*/
- boolean isCurrentEmergencyNumber(String number);
+ boolean isCurrentEmergencyNumber(String number, boolean exactMatch);
/**
* Return a list of certs in hex string from loaded carrier privileges access rules.
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 76e7509..d9f5c3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -81,5 +81,5 @@
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
void notifyPreferredDataSubIdChanged(int preferredSubId);
void notifyRadioPowerStateChanged(in int state);
- void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList);
+ void notifyEmergencyNumberList();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cb8269e..c934317 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -483,4 +483,5 @@
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
+ int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 4d765d3..157609c 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -37,8 +37,6 @@
"junit.framework",
],
- droiddoc_options: ["-stubsourceonly"],
- metalava_enabled: false,
compile_dex: true,
}
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index b917fbd..0ac35bc 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -54,10 +54,10 @@
*/
private class InversionIContentProvider implements IContentProvider {
@Override
- public ContentProviderResult[] applyBatch(String callingPackage,
+ public ContentProviderResult[] applyBatch(String callingPackage, String authority,
ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
- return MockContentProvider.this.applyBatch(operations);
+ return MockContentProvider.this.applyBatch(authority, operations);
}
@Override
@@ -112,9 +112,9 @@
}
@Override
- public Bundle call(String callingPackage, String method, String request, Bundle args)
- throws RemoteException {
- return MockContentProvider.this.call(method, request, args);
+ public Bundle call(String callingPackage, String authority, String method, String request,
+ Bundle args) throws RemoteException {
+ return MockContentProvider.this.call(authority, method, request, args);
}
@Override
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 112d7ee..fc2a464 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -80,7 +80,7 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPackage,
+ public ContentProviderResult[] applyBatch(String callingPackage, String authority,
ArrayList<ContentProviderOperation> operations) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -103,8 +103,8 @@
}
@Override
- public Bundle call(String callingPackage, String method, String request, Bundle args)
- throws RemoteException {
+ public Bundle call(String callingPackage, String authority, String method, String request,
+ Bundle args) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 0a0d50c..db5053e 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -40,8 +40,6 @@
"junit.textui",
],
- droiddoc_options: ["-stubsourceonly"],
- metalava_enabled: false,
compile_dex: true
}
diff --git a/test-runner/api/current.txt b/test-runner/api/current.txt
index 1170eb5..4ba1b8f 100644
--- a/test-runner/api/current.txt
+++ b/test-runner/api/current.txt
@@ -125,8 +125,8 @@
method public static void assertEquals(double[], double[]);
method public static void assertEquals(java.lang.String, java.lang.Object[], java.lang.Object[]);
method public static void assertEquals(java.lang.Object[], java.lang.Object[]);
- method public static void assertEquals(java.lang.String, java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
- method public static void assertEquals(java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
+ method public static void assertEquals(java.lang.String, java.util.Set<?>, java.util.Set<?>);
+ method public static void assertEquals(java.util.Set<?>, java.util.Set<?>);
method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String, java.lang.String);
method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String);
method public static void assertNotContainsRegex(java.lang.String, java.lang.String, java.lang.String);
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index d62ef9e..ca25386 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -13,7 +13,8 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <service android:name="SchedulerService">
+ <service android:name="SchedulerService"
+ android:foregroundServiceType="sync">
</service>
<service android:name="TestService" android:process=":test">
</service>
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
index fc3f390..d4cbbf9 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
@@ -17,16 +17,22 @@
package com.android.frameworkperf;
import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class SchedulerService extends Service {
+ private static final String NOTIFICATION_CHANNEL_ID = SchedulerService.class.getSimpleName();
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- Notification status = new Notification.Builder(this)
+ getSystemService(NotificationManager.class).createNotificationChannel(
+ new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
+ NotificationManager.IMPORTANCE_DEFAULT));
+ Notification status = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.stat_happy)
.setWhen(System.currentTimeMillis())
.setContentTitle("Scheduler Test running")
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index be74a6d..7a5e732 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -92,11 +92,11 @@
event.mPackage = "fake.package.name" + pkg;
event.mClass = event.mPackage + ".class1";
event.mTimeStamp = 1;
- event.mEventType = UsageEvents.Event.MOVE_TO_FOREGROUND;
+ event.mEventType = UsageEvents.Event.ACTIVITY_RESUMED;
for (int evt = 0; evt < eventsPerPackage; evt++) {
intervalStats.events.insert(event);
intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp,
- event.mEventType);
+ event.mEventType, 1);
}
}
}
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
index 3480e96..53afa26 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
@@ -21,13 +21,14 @@
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
-import androidx.collection.CircularArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
+import androidx.collection.CircularArray;
+
public class UsageLogActivity extends ListActivity implements Runnable {
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
@@ -155,10 +156,10 @@
private String eventToString(int eventType) {
switch (eventType) {
- case UsageEvents.Event.MOVE_TO_FOREGROUND:
+ case UsageEvents.Event.ACTIVITY_RESUMED:
return "Foreground";
- case UsageEvents.Event.MOVE_TO_BACKGROUND:
+ case UsageEvents.Event.ACTIVITY_PAUSED:
return "Background";
case UsageEvents.Event.CONFIGURATION_CHANGE:
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 132135d..9d1edbf 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -51,7 +51,6 @@
liblog \
liblzma \
libnativehelper \
- libnetdaidl \
libpackagelistparser \
libpcre2 \
libselinux \
@@ -93,7 +92,6 @@
liblog \
libcutils \
libnativehelper \
- libnetdaidl \
netd_aidl_interface-cpp
LOCAL_STATIC_LIBRARIES := \
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index b40921f..84f7359 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -24,9 +24,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
@@ -46,7 +46,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
-
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -457,6 +456,69 @@
assertEquals(nc1, nc2);
}
+ @Test
+ public void testSetNetworkSpecifierOnMultiTransportNc() {
+ // Sequence 1: Transport + Transport + NetworkSpecifier
+ NetworkCapabilities nc1 = new NetworkCapabilities();
+ nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI);
+ try {
+ nc1.setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+ fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
+ } catch (IllegalStateException expected) {
+ // empty
+ }
+
+ // Sequence 2: Transport + NetworkSpecifier + Transport
+ NetworkCapabilities nc2 = new NetworkCapabilities();
+ nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier(
+ new StringNetworkSpecifier("specs"));
+ try {
+ nc2.addTransportType(TRANSPORT_WIFI);
+ fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!");
+ } catch (IllegalStateException expected) {
+ // empty
+ }
+ }
+
+ @Test
+ public void testSetTransportInfoOnMultiTransportNc() {
+ // Sequence 1: Transport + Transport + TransportInfo
+ NetworkCapabilities nc1 = new NetworkCapabilities();
+ nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new TransportInfo() {});
+
+ // Sequence 2: Transport + NetworkSpecifier + Transport
+ NetworkCapabilities nc2 = new NetworkCapabilities();
+ nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TransportInfo() {})
+ .addTransportType(TRANSPORT_WIFI);
+ }
+
+ @Test
+ public void testCombineTransportInfo() {
+ NetworkCapabilities nc1 = new NetworkCapabilities();
+ nc1.setTransportInfo(new TransportInfo() {
+ // empty
+ });
+ NetworkCapabilities nc2 = new NetworkCapabilities();
+ // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
+ // combine fails)
+ nc2.setTransportInfo(new TransportInfo() {
+ // empty
+ });
+
+ try {
+ nc1.combineCapabilities(nc2);
+ fail("Should not be able to combine NetworkCabilities which contain TransportInfos");
+ } catch (IllegalStateException expected) {
+ // empty
+ }
+
+ // verify that can combine with identical TransportInfo objects
+ NetworkCapabilities nc3 = new NetworkCapabilities();
+ nc3.setTransportInfo(nc1.getTransportInfo());
+ nc1.combineCapabilities(nc3);
+ }
+
private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
Parcel p = Parcel.obtain();
netCap.writeToParcel(p, /* flags */ 0);
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index d6dbf5a..1e3a49b 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -448,22 +448,58 @@
}
@Test
- public void testWithoutUid() throws Exception {
- final NetworkStats before = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+ public void testRemoveUids() throws Exception {
+ final NetworkStats before = new NetworkStats(TEST_START, 3);
- final NetworkStats after = before.withoutUids(new int[] { 100 });
- assertEquals(6, before.size());
- assertEquals(2, after.size());
- assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
- assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
+ // Test 0 item stats.
+ NetworkStats after = before.clone();
+ after.removeUids(new int[0]);
+ assertEquals(0, after.size());
+ after.removeUids(new int[] {100});
+ assertEquals(0, after.size());
+
+ // Test 1 item stats.
+ before.addValues(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+ after = before.clone();
+ after.removeUids(new int[0]);
+ assertEquals(1, after.size());
+ assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+ after.removeUids(new int[] {99});
+ assertEquals(0, after.size());
+
+ // Append remaining test items.
+ before.addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+ assertEquals(7, before.size());
+
+ // Test remove with empty uid list.
+ after = before.clone();
+ after.removeUids(new int[0]);
+ assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L);
+
+ // Test remove uids don't exist in stats.
+ after.removeUids(new int[] {98, 0, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L);
+
+ // Test remove all uids.
+ after.removeUids(new int[] {99, 100, 100, 101});
+ assertEquals(0, after.size());
+
+ // Test remove in the middle.
+ after = before.clone();
+ after.removeUids(new int[] {100});
+ assertEquals(3, after.size());
+ assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+ assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 32L, 4L, 0L, 0L, 0L);
+ assertValues(after, 2, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 64L, 2L, 0L, 0L, 0L);
}
@Test
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index 5a8d2cd..cba3c65 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -81,7 +81,7 @@
private static final int TEST_IFINDEX = 1001;
// See RFC 7042#section-2.1.2 for EUI-48 documentation values.
private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01");
- private static final int TEST_TIMEOUT_MS = 200;
+ private static final int TEST_TIMEOUT_MS = 400;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 17bcea0..71529fd 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -96,6 +96,7 @@
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
+import android.net.INetd;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
@@ -228,6 +229,7 @@
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@Mock INetworkPolicyManager mNpm;
+ @Mock INetd mMockNetd;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -926,8 +928,9 @@
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IpConnectivityLog log) {
+ IpConnectivityLog log, INetd netd) {
super(context, netManager, statsService, policyManager, log);
+ mNetd = netd;
mLingerDelayMs = TEST_LINGER_DELAY_MS;
}
@@ -1087,7 +1090,8 @@
mNetworkManagementService,
mStatsService,
mNpm,
- mock(IpConnectivityLog.class));
+ mock(IpConnectivityLog.class),
+ mMockNetd);
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -4766,4 +4770,30 @@
// Clean up
mCm.unregisterNetworkCallback(networkCallback);
}
+
+ private static final String TEST_TCP_BUFFER_SIZES = "1,2,3,4,5,6";
+
+ private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception {
+ String[] values = tcpBufferSizes.split(",");
+ String rmemValues = String.join(" ", values[0], values[1], values[2]);
+ String wmemValues = String.join(" ", values[3], values[4], values[5]);
+ waitForIdle();
+ verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
+ reset(mMockNetd);
+ }
+
+ @Test
+ public void testTcpBufferReset() throws Exception {
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ reset(mMockNetd);
+ // Simple connection should have updated tcp buffer size.
+ mCellNetworkAgent.connect(false);
+ verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
+
+ // Change link Properties should have updated tcp buffer size.
+ LinkProperties lp = new LinkProperties();
+ lp.setTcpBufferSizes(TEST_TCP_BUFFER_SIZES);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES);
+ }
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 4dc0341..d8f9618 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -542,7 +542,7 @@
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
+ eq(pfd),
eq(mUid),
eq(IpSecManager.DIRECTION_OUT),
anyString(),
@@ -555,7 +555,7 @@
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
mIpSecService.removeTransportModeTransforms(pfd);
- verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+ verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
}
private IpSecTunnelInterfaceResponse createAndValidateTunnel(
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 2c94a60..724446e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -425,7 +425,7 @@
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
mIpSecService.removeTransportModeTransforms(pfd);
- verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+ verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
}
@Test
@@ -620,10 +620,10 @@
mIpSecService.openUdpEncapsulationSocket(0, new Binder());
FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
- ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
+ ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
try {
StructStat sockStat = Os.fstat(sockFd);
- StructStat argStat = Os.fstat(arg);
+ StructStat argStat = Os.fstat(arg.getFileDescriptor());
return sockStat.st_ino == argStat.st_ino
&& sockStat.st_dev == argStat.st_dev;
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 583f14a..9460c9e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -306,31 +306,6 @@
break;
}
- for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) {
- printer->Print((i == 0) ? " " : "|");
- printer->Print("OVERLAYABLE");
-
- if (entry->overlayable_declarations[i].policy) {
- switch (entry->overlayable_declarations[i].policy.value()) {
- case Overlayable::Policy::kProduct:
- printer->Print("_PRODUCT");
- break;
- case Overlayable::Policy::kProductServices:
- printer->Print("_PRODUCT_SERVICES");
- break;
- case Overlayable::Policy::kSystem:
- printer->Print("_SYSTEM");
- break;
- case Overlayable::Policy::kVendor:
- printer->Print("_VENDOR");
- break;
- case Overlayable::Policy::kPublic:
- printer->Print("_PUBLIC");
- break;
- }
- }
- }
-
printer->Println();
if (options.show_values) {
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 4f25e09..9587704 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@
ResourceId id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
bool allow_new = false;
- std::vector<Overlayable> overlayable_declarations;
+ Maybe<Overlayable> overlayable;
std::string comment;
std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@
}
}
- for (auto& overlayable : res->overlayable_declarations) {
- if (!table->AddOverlayable(res->name, overlayable, diag)) {
+ if (res->overlayable) {
+ if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
return false;
}
}
@@ -1063,20 +1063,19 @@
<< "' for <overlayable> tag");
}
- std::string comment;
- std::vector<Overlayable::Policy> policies;
-
bool error = false;
+ std::string comment;
+ Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
const size_t start_depth = parser->depth();
while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
xml::XmlPullParser::Event event = parser->event();
if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
- // Break the loop when exiting the overyabale element
+ // Break the loop when exiting the overlayable element
break;
} else if (event == xml::XmlPullParser::Event::kEndElement
&& parser->depth() == start_depth + 1) {
// Clear the current policies when exiting the policy element
- policies.clear();
+ current_policies = Overlayable::Policy::kNone;
continue;
} else if (event == xml::XmlPullParser::Event::kComment) {
// Get the comment of individual item elements
@@ -1090,43 +1089,71 @@
const Source item_source = source_.WithLine(parser->line_number());
const std::string& element_name = parser->element_name();
const std::string& element_namespace = parser->element_namespace();
-
if (element_namespace.empty() && element_name == "item") {
- if (!ParseOverlayableItem(parser, policies, comment, out_resource)) {
+ // Items specify the name and type of resource that should be overlayable
+ Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> within an <overlayable> tag must have a 'name' attribute");
error = true;
+ continue;
}
+
+ Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!maybe_type) {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> within an <overlayable> tag must have a 'type' attribute");
+ error = true;
+ continue;
+ }
+
+ const ResourceType* type = ParseResourceType(maybe_type.value());
+ if (type == nullptr) {
+ diag_->Error(DiagMessage(item_source)
+ << "invalid resource type '" << maybe_type.value()
+ << "' in <item> within an <overlayable>");
+ error = true;
+ continue;
+ }
+
+ ParsedResource child_resource;
+ child_resource.name.type = *type;
+ child_resource.name.entry = maybe_name.value().to_string();
+ child_resource.overlayable = Overlayable{current_policies, item_source, comment};
+ out_resource->child_resources.push_back(std::move(child_resource));
+
} else if (element_namespace.empty() && element_name == "policy") {
- if (!policies.empty()) {
+ if (current_policies != Overlayable::Policy::kNone) {
// If the policy list is not empty, then we are currently inside a policy element
diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
error = true;
break;
} else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
- // policies at once
+ // policies
for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
if (trimmed_part == "public") {
- policies.push_back(Overlayable::Policy::kPublic);
+ current_policies |= Overlayable::Policy::kPublic;
} else if (trimmed_part == "product") {
- policies.push_back(Overlayable::Policy::kProduct);
+ current_policies |= Overlayable::Policy::kProduct;
} else if (trimmed_part == "product_services") {
- policies.push_back(Overlayable::Policy::kProductServices);
+ current_policies |= Overlayable::Policy::kProductServices;
} else if (trimmed_part == "system") {
- policies.push_back(Overlayable::Policy::kSystem);
+ current_policies |= Overlayable::Policy::kSystem;
} else if (trimmed_part == "vendor") {
- policies.push_back(Overlayable::Policy::kVendor);
+ current_policies |= Overlayable::Policy::kVendor;
} else {
- diag_->Error(DiagMessage(out_resource->source)
- << "<policy> has unsupported type '" << trimmed_part << "'");
+ diag_->Error(DiagMessage(item_source)
+ << "<policy> has unsupported type '" << trimmed_part << "'");
error = true;
continue;
}
}
}
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
- diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in "
- << " <overlayable>");
+ diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
+ << " in <overlayable>");
error = true;
break;
}
@@ -1135,61 +1162,6 @@
return !error;
}
-bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser,
- const std::vector<Overlayable::Policy>& policies,
- const std::string& comment,
- ParsedResource* out_resource) {
- const Source item_source = source_.WithLine(parser->line_number());
-
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
- if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'name' attribute");
- return false;
- }
-
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
- if (!maybe_type) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'type' attribute");
- return false;
- }
-
- const ResourceType* type = ParseResourceType(maybe_type.value());
- if (type == nullptr) {
- diag_->Error(DiagMessage(out_resource->source)
- << "invalid resource type '" << maybe_type.value()
- << "' in <item> within an <overlayable>");
- return false;
- }
-
- ParsedResource child_resource;
- child_resource.name.type = *type;
- child_resource.name.entry = maybe_name.value().to_string();
- child_resource.source = item_source;
-
- if (policies.empty()) {
- Overlayable overlayable;
- overlayable.source = item_source;
- overlayable.comment = comment;
- child_resource.overlayable_declarations.push_back(overlayable);
- } else {
- for (Overlayable::Policy policy : policies) {
- Overlayable overlayable;
- overlayable.policy = policy;
- overlayable.source = item_source;
- overlayable.comment = comment;
- child_resource.overlayable_declarations.push_back(overlayable);
- }
- }
-
- if (options_.visibility) {
- child_resource.visibility_level = options_.visibility.value();
- }
- out_resource->child_resources.push_back(std::move(child_resource));
- return true;
-}
-
bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (ParseSymbolImpl(parser, out_resource)) {
out_resource->visibility_level = Visibility::Level::kUndefined;
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index ebacd6f..06bb0c9 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -96,10 +96,6 @@
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool ParseOverlayableItem(xml::XmlPullParser* parser,
- const std::vector<Overlayable::Policy>& policies,
- const std::string& comment,
- ParsedResource* out_resource);
bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index c6f29ac..03e6197 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -905,16 +905,16 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
+ Eq(Overlayable::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
+ Eq(Overlayable::Policy::kNone));
}
TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
@@ -945,49 +945,44 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kPublic));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
}
TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
@@ -1031,22 +1026,18 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kVendor));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
+ | Overlayable::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
+ | Overlayable::Policy::kSystem));
}
TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
@@ -1067,7 +1058,7 @@
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable">
+ <overlayable>
<policy type="product">
<item type="string" name="foo" />
<item type="string" name="foo" />
@@ -1080,6 +1071,26 @@
<policy type="product">
<item type="string" name="foo" />
</policy>
+ <item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable>
+ <policy type="product">
+ <item type="string" name="foo" />
+ </policy>
+ <policy type="vendor">
+ <item type="string" name="foo" />
+ </policy>
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable>
+ <policy type="product">
+ <item type="string" name="foo" />
+ </policy>
</overlayable>
<overlayable>
@@ -1090,41 +1101,6 @@
EXPECT_FALSE(TestParse(input));
}
-TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) {
- std::string input = R"(
- <overlayable policy="product">
- <item type="string" name="foo" />
- </overlayable>
- <overlayable policy="">
- <item type="string" name="foo" />
- </overlayable>)";
- EXPECT_FALSE(TestParse(input));
-
- input = R"(
- <overlayable policy="">
- <item type="string" name="foo" />
- </overlayable>
- <overlayable policy="product">
- <item type="string" name="foo" />
- </overlayable>)";
- EXPECT_FALSE(TestParse(input));
-}
-
-TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) {
- std::string input = R"(
- <overlayable>
- <policy type="vendor|product">
- <item type="string" name="foo" />
- </policy>
- </overlayable>
- <overlayable>
- <policy type="product_services|vendor">
- <item type="string" name="foo" />
- </policy>
- </overlayable>)";
- EXPECT_FALSE(TestParse(input));
-}
-
TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
std::string input = R"(
<overlayable>
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index bc8a4d1..54633ad 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -625,18 +625,18 @@
return true;
}
-bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
IDiagnostics* diag) {
- return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
+ return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
}
-bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name,
+bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
const Overlayable& overlayable, IDiagnostics* diag) {
- return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag);
+ return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
}
-bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
- NameValidator name_validator, IDiagnostics* diag) {
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+ NameValidator name_validator, IDiagnostics *diag) {
CHECK(diag != nullptr);
if (!ValidateName(name_validator, name, overlayable.source, diag)) {
@@ -647,27 +647,14 @@
ResourceTableType* type = package->FindOrCreateType(name.type);
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
- for (auto& overlayable_declaration : entry->overlayable_declarations) {
- // An overlayable resource cannot be declared twice with the same policy
- if (overlayable.policy == overlayable_declaration.policy) {
- diag->Error(DiagMessage(overlayable.source)
+ if (entry->overlayable) {
+ diag->Error(DiagMessage(overlayable.source)
<< "duplicate overlayable declaration for resource '" << name << "'");
- diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
- return false;
- }
-
- // An overlayable resource cannot be declared once with a policy and without a policy because
- // the policy becomes unused
- if (!overlayable.policy || !overlayable_declaration.policy) {
- diag->Error(DiagMessage(overlayable.source)
- << "overlayable resource '" << name << "'"
- << " declared once with a policy and once with no policy");
- diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
- return false;
- }
+ diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+ return false;
}
- entry->overlayable_declarations.push_back(overlayable);
+ entry->overlayable = overlayable;
return true;
}
@@ -703,7 +690,7 @@
new_entry->id = entry->id;
new_entry->visibility = entry->visibility;
new_entry->allow_new = entry->allow_new;
- new_entry->overlayable_declarations = entry->overlayable_declarations;
+ new_entry->overlayable = entry->overlayable;
for (const auto& config_value : entry->values) {
ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 3dd0a769..e646f5b 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,27 +57,32 @@
std::string comment;
};
-// Represents a declaration that a resource is overayable at runtime.
+// Represents a declaration that a resource is overlayable at runtime.
struct Overlayable {
+
// Represents the types overlays that are allowed to overlay the resource.
- enum class Policy {
+ enum Policy : uint32_t {
+ kNone = 0x00,
+
// The resource can be overlaid by any overlay.
- kPublic,
+ kPublic = 0x01,
// The resource can be overlaid by any overlay on the system partition.
- kSystem,
+ kSystem = 0x02,
// The resource can be overlaid by any overlay on the vendor partition.
- kVendor,
+ kVendor = 0x04,
// The resource can be overlaid by any overlay on the product partition.
- kProduct,
+ kProduct = 0x08,
// The resource can be overlaid by any overlay on the product services partition.
- kProductServices,
+ kProductServices = 0x10
};
- Maybe<Policy> policy;
+ typedef uint32_t PolicyFlags;
+ PolicyFlags policies = Policy::kNone;
+
Source source;
std::string comment;
};
@@ -116,7 +121,7 @@
Maybe<AllowNew> allow_new;
// The declarations of this resource as overlayable for RROs
- std::vector<Overlayable> overlayable_declarations;
+ Maybe<Overlayable> overlayable;
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -246,9 +251,9 @@
bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
const ResourceId& res_id, IDiagnostics* diag);
- bool AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
- IDiagnostics* diag);
- bool AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+ bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+ IDiagnostics *diag);
+ bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
IDiagnostics* diag);
bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -323,8 +328,8 @@
bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
NameValidator name_validator, IDiagnostics* diag);
- bool AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
- NameValidator name_validator, IDiagnostics* diag);
+ bool SetOverlayableImpl(const ResourceNameRef &name, const Overlayable &overlayable,
+ NameValidator name_validator, IDiagnostics *diag);
bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
const Visibility& symbol, NameValidator name_validator,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 7c28f07..31095c4 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -242,69 +242,50 @@
ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second"));
}
-TEST(ResourceTableTest, AddOverlayable) {
+TEST(ResourceTableTest, SetOverlayable) {
ResourceTable table;
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ overlayable.policies |= Overlayable::Policy::kProductServices;
+ overlayable.comment = "comment";
+
const ResourceName name = test::ParseNameOrDie("android:string/foo");
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
- Overlayable overlayable;
- overlayable.policy = Overlayable::Policy::kProduct;
- overlayable.comment = "first";
- ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
- Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable overlayable2;
- overlayable2.comment = "second";
- overlayable2.policy = Overlayable::Policy::kProductServices;
- ASSERT_TRUE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
- result = table.FindResource(name);
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
- ASSERT_THAT(result.value().entry->overlayable_declarations[1].comment, StrEq("second"));
- ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProductServices));
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ ASSERT_THAT(result_overlayable.comment, StrEq("comment"));
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
+ | Overlayable::Policy::kProductServices));
}
-TEST(ResourceTableTest, AddDuplicateOverlayableFail) {
+TEST(ResourceTableTest, AddDuplicateOverlayableSamePolicyFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- Overlayable overlayable;
- overlayable.policy = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
+ Overlayable overlayable{};
+ overlayable.policies = Overlayable::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
- Overlayable overlayable2;
- overlayable2.policy = Overlayable::Policy::kProduct;
- ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+ Overlayable overlayable2{};
+ overlayable2.policies = Overlayable::Policy::kProduct;
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
}
-TEST(ResourceTableTest, AddOverlayablePolicyAndNoneFirstFail) {
+TEST(ResourceTableTest, AddDuplicateOverlayableDifferentPolicyFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- ASSERT_TRUE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
+ Overlayable overlayable{};
+ overlayable.policies = Overlayable::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
- Overlayable overlayable2;
- overlayable2.policy = Overlayable::Policy::kProduct;
- ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
-}
-
-TEST(ResourceTableTest, AddOverlayablePolicyAndNoneLastFail) {
- ResourceTable table;
- const ResourceName name = test::ParseNameOrDie("android:string/foo");
-
- Overlayable overlayable;
- overlayable.policy = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
-
- ASSERT_FALSE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
+ Overlayable overlayable2{};
+ overlayable2.policies = Overlayable::Policy::kVendor;
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
}
TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index bf9fe49..81a2c2e 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -136,12 +136,11 @@
// Represents a declaration that a resource is overayable at runtime.
message Overlayable {
enum Policy {
- NONE = 0;
- PUBLIC = 1;
- SYSTEM = 2;
- VENDOR = 3;
- PRODUCT = 4;
- PRODUCT_SERVICES = 5;
+ PUBLIC = 0;
+ SYSTEM = 1;
+ VENDOR = 2;
+ PRODUCT = 3;
+ PRODUCT_SERVICES = 4;
}
// Where this declaration was defined in source.
@@ -150,8 +149,8 @@
// Any comment associated with the declaration.
string comment = 2;
- // The policy of the overlayable declaration
- Policy policy = 3;
+ // The policy defined in the overlayable declaration.
+ repeated Policy policy = 3;
}
// An entry ID in the range [0x0000, 0xffff].
@@ -181,7 +180,7 @@
AllowNew allow_new = 4;
// Whether this resource can be overlaid by a runtime resource overlay (RRO).
- repeated Overlayable overlayable = 5;
+ Overlayable overlayable = 5;
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 89d19cf..5cf056e 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -255,6 +255,7 @@
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpBadgerCommand>(printer), /* hidden */ true);
+ // TODO(b/120609160): Add aapt2 overlayable dump command
}
int Action(const std::vector<std::string>& args) override {
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 3a71e83..2ef8b99 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -230,7 +230,7 @@
test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de")));
ASSERT_TRUE(a1.android_sdk);
ASSERT_TRUE(a1.android_sdk.value().min_sdk_version);
- EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19L);
EXPECT_THAT(a1.textures, SizeIs(1ul));
EXPECT_THAT(a1.features, SizeIs(1ul));
@@ -250,7 +250,7 @@
test::ParseConfigOrDie("fr-rCA")));
ASSERT_TRUE(a2.android_sdk);
ASSERT_TRUE(a2.android_sdk.value().min_sdk_version);
- EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19L);
EXPECT_THAT(a2.textures, SizeIs(1ul));
EXPECT_THAT(a2.features, SizeIs(1ul));
}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index ed70fb3..61ebd4e 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -240,6 +240,12 @@
}
break;
+ case android::RES_TABLE_OVERLAYABLE_TYPE:
+ if (!ParseOverlayable(parser.chunk())) {
+ return false;
+ }
+ break;
+
default:
diag_->Warn(DiagMessage(source_)
<< "unexpected chunk type "
@@ -383,24 +389,12 @@
return false;
}
- const uint32_t type_spec_flags = entry_type_spec_flags_[res_id];
- if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0 ||
- (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) != 0) {
- if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
- Visibility visibility;
- visibility.level = Visibility::Level::kPublic;
- visibility.source = source_.WithLine(0);
- if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
- return false;
- }
- }
-
- if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) {
- Overlayable overlayable;
- overlayable.source = source_.WithLine(0);
- if (!table_->AddOverlayableMangled(name, overlayable, diag_)) {
- return false;
- }
+ if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
+ Visibility visibility;
+ visibility.level = Visibility::Level::kPublic;
+ visibility.source = source_.WithLine(0);
+ if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
+ return false;
}
// Erase the ID from the map once processed, so that we don't mark the same symbol more than
@@ -433,6 +427,70 @@
return true;
}
+bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
+ const ResTable_overlayable_header* header = ConvertTo<ResTable_overlayable_header>(chunk);
+ if (!header) {
+ diag_->Error(DiagMessage(source_) << "corrupt ResTable_category_header chunk");
+ return false;
+ }
+
+ ResChunkPullParser parser(GetChunkData(chunk),
+ GetChunkDataLen(chunk));
+ while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+ if (util::DeviceToHost16(parser.chunk()->type) == android::RES_TABLE_OVERLAYABLE_POLICY_TYPE) {
+ const ResTable_overlayable_policy_header* policy_header =
+ ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
+
+ Overlayable::PolicyFlags policies = Overlayable::Policy::kNone;
+ if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
+ policies |= Overlayable::Policy::kPublic;
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
+ policies |= Overlayable::Policy::kSystem;
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
+ policies |= Overlayable::Policy::kVendor;
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
+ policies |= Overlayable::Policy::kProduct;
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
+ policies |= Overlayable::Policy::kProductServices;
+ }
+
+ const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
+ ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
+ const ResTable_ref* const ref_end = ref_begin
+ + util::DeviceToHost32(policy_header->entry_count);
+ for (auto ref_iter = ref_begin; ref_iter != ref_end; ++ref_iter) {
+ ResourceId res_id(util::DeviceToHost32(ref_iter->ident));
+ const auto iter = id_index_.find(res_id);
+
+ // If the overlayable chunk comes before the type chunks, the resource ids and resource name
+ // pairing will not exist at this point.
+ if (iter == id_index_.cend()) {
+ diag_->Error(DiagMessage(source_) << "failed to find resource name for overlayable"
+ << " resource " << res_id);
+ return false;
+ }
+
+ Overlayable overlayable{};
+ overlayable.source = source_.WithLine(0);
+ overlayable.policies = policies;
+ if (!table_->SetOverlayable(iter->second, overlayable, diag_)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
const ConfigDescription& config,
const android::Res_value& value) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index 2bdc051..a2eee50 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -54,6 +54,7 @@
bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseLibrary(const android::ResChunk_header* chunk);
+ bool ParseOverlayable(const android::ResChunk_header* chunk);
std::unique_ptr<Item> ParseValue(const ResourceNameRef& name,
const android::ConfigDescription& config,
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 6c1a9ba..200e2d4 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -24,6 +24,7 @@
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceUtils.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
@@ -216,6 +217,11 @@
size_t entry_count_ = 0;
};
+struct PolicyChunk {
+ uint32_t policy_flags;
+ std::set<ResourceId> ids;
+};
+
class PackageFlattener {
public:
PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
@@ -267,6 +273,8 @@
FlattenLibrarySpec(buffer);
}
+ FlattenOverlayable(buffer);
+
pkg_writer.Finish();
return true;
}
@@ -413,6 +421,93 @@
return sorted_entries;
}
+ void FlattenOverlayable(BigBuffer* buffer) {
+ std::vector<PolicyChunk> policies;
+
+ CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
+ for (auto& type : package_->types) {
+ CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
+ for (auto& entry : type->entries) {
+ CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
+ if (!entry->overlayable) {
+ continue;
+ }
+
+ Overlayable overlayable = entry->overlayable.value();
+ uint32_t policy_flags = Overlayable::Policy::kNone;
+ if (overlayable.policies & Overlayable::Policy::kPublic) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+ }
+ if (overlayable.policies & Overlayable::Policy::kSystem) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+ }
+ if (overlayable.policies & Overlayable::Policy::kVendor) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+ }
+ if (overlayable.policies & Overlayable::Policy::kProduct) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+ }
+ if (overlayable.policies & Overlayable::Policy::kProductServices) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+ }
+
+ if (overlayable.policies == Overlayable::Policy::kNone) {
+ // Encode overlayable entries defined without a policy as publicly overlayable
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+ }
+
+ // Find the overlayable policy chunk with the same policies as the entry
+ PolicyChunk* policy_chunk = nullptr;
+ for (PolicyChunk& policy : policies) {
+ if (policy.policy_flags == policy_flags) {
+ policy_chunk = &policy;
+ break;
+ }
+ }
+
+ // Create a new policy chunk if an existing one with the same policy cannot be found
+ if (policy_chunk == nullptr) {
+ PolicyChunk p;
+ p.policy_flags = policy_flags;
+ policies.push_back(p);
+ policy_chunk = &policies.back();
+ }
+
+ policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
+ entry->id.value()));
+ }
+ }
+
+ if (policies.empty()) {
+ // Only write the overlayable chunk if the APK has overlayable entries
+ return;
+ }
+
+ ChunkWriter writer(buffer);
+ writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
+
+ // Write each policy block for the overlayable
+ for (PolicyChunk& policy : policies) {
+ ChunkWriter policy_writer(buffer);
+ ResTable_overlayable_policy_header* policy_type =
+ policy_writer.StartChunk<ResTable_overlayable_policy_header>(
+ RES_TABLE_OVERLAYABLE_POLICY_TYPE);
+ policy_type->policy_flags = util::HostToDevice32(policy.policy_flags);
+ policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size()));
+
+ // Write the ids after the policy header
+ ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size());
+ for (const ResourceId& id : policy.ids) {
+ id_block->ident = util::HostToDevice32(id.id);
+ id_block++;
+ }
+
+ policy_writer.Finish();
+ }
+
+ writer.Finish();
+ }
+
bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
@@ -446,11 +541,6 @@
config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
- if (!entry->overlayable_declarations.empty()) {
- config_masks[entry->id.value()] |=
- util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE);
- }
-
const size_t config_count = entry->values.size();
for (size_t i = 0; i < config_count; i++) {
const ConfigDescription& config = entry->values[i]->config;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index cd1414c..e99ab1f 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,24 +628,101 @@
}
TEST_F(TableFlattenerTest, FlattenOverlayable) {
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ overlayable.policies |= Overlayable::Policy::kSystem;
+ overlayable.policies |= Overlayable::Policy::kVendor;
+
+ std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
- .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000))
+ .AddSimple(name, ResourceId(0x7f020000))
+ .SetOverlayable(name, overlayable)
.Build();
- ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
- Overlayable{}, test::GetDiagnostics()));
+ ResourceTable output_table;
+ ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
- ResTable res_table;
- ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
+ auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
+ | Overlayable::Policy::kVendor
+ | Overlayable::Policy::kProduct);
+}
- const StringPiece16 overlayable_name(u"com.app.test:integer/overlayable");
- uint32_t spec_flags = 0u;
- ASSERT_THAT(res_table.identifierForName(overlayable_name.data(), overlayable_name.size(), nullptr,
- 0u, nullptr, 0u, &spec_flags),
- Gt(0u));
- EXPECT_TRUE(spec_flags & android::ResTable_typeSpec::SPEC_OVERLAYABLE);
+TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
+ std::string name_zero = "com.app.test:integer/overlayable_zero";
+ Overlayable overlayable_zero{};
+ overlayable_zero.policies |= Overlayable::Policy::kProduct;
+ overlayable_zero.policies |= Overlayable::Policy::kSystem;
+ overlayable_zero.policies |= Overlayable::Policy::kProductServices;
+
+ std::string name_one = "com.app.test:integer/overlayable_one";
+ Overlayable overlayable_one{};
+ overlayable_one.policies |= Overlayable::Policy::kPublic;
+ overlayable_one.policies |= Overlayable::Policy::kProductServices;
+
+ std::string name_two = "com.app.test:integer/overlayable_two";
+ Overlayable overlayable_two{};
+ overlayable_two.policies |= Overlayable::Policy::kProduct;
+ overlayable_two.policies |= Overlayable::Policy::kSystem;
+ overlayable_two.policies |= Overlayable::Policy::kVendor;
+
+ std::string name_three = "com.app.test:integer/overlayable_three";
+ Overlayable overlayable_three{};
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple(name_zero, ResourceId(0x7f020000))
+ .SetOverlayable(name_zero, overlayable_zero)
+ .AddSimple(name_one, ResourceId(0x7f020001))
+ .SetOverlayable(name_one, overlayable_one)
+ .AddSimple(name_two, ResourceId(0x7f020002))
+ .SetOverlayable(name_two, overlayable_two)
+ .AddSimple(name_three, ResourceId(0x7f020003))
+ .SetOverlayable(name_three, overlayable_three)
+ .Build();
+
+ ResourceTable output_table;
+ ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
+
+ auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
+ | Overlayable::Policy::kProduct
+ | Overlayable::Policy::kProductServices);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic
+ | Overlayable::Policy::kProductServices);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
+ | Overlayable::Policy::kProduct
+ | Overlayable::Policy::kVendor);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic);
}
} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index f612914..cf2ab0f 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -437,37 +437,39 @@
entry->allow_new = std::move(allow_new);
}
- for (const pb::Overlayable& pb_overlayable : pb_entry.overlayable()) {
- Overlayable overlayable;
- switch (pb_overlayable.policy()) {
- case pb::Overlayable::NONE:
- overlayable.policy = {};
- break;
- case pb::Overlayable::PUBLIC:
- overlayable.policy = Overlayable::Policy::kPublic;
- break;
- case pb::Overlayable::PRODUCT:
- overlayable.policy = Overlayable::Policy::kProduct;
- break;
- case pb::Overlayable::PRODUCT_SERVICES:
- overlayable.policy = Overlayable::Policy::kProductServices;
- break;
- case pb::Overlayable::SYSTEM:
- overlayable.policy = Overlayable::Policy::kSystem;
- break;
- case pb::Overlayable::VENDOR:
- overlayable.policy = Overlayable::Policy::kVendor;
- break;
- default:
- *out_error = "unknown overlayable policy";
- return false;
+ if (pb_entry.has_overlayable()) {
+ Overlayable overlayable{};
+
+ const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
+ for (const int policy : pb_overlayable.policy()) {
+ switch (policy) {
+ case pb::Overlayable::PUBLIC:
+ overlayable.policies |= Overlayable::Policy::kPublic;
+ break;
+ case pb::Overlayable::SYSTEM:
+ overlayable.policies |= Overlayable::Policy::kSystem;
+ break;
+ case pb::Overlayable::VENDOR:
+ overlayable.policies |= Overlayable::Policy::kVendor;
+ break;
+ case pb::Overlayable::PRODUCT:
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ break;
+ case pb::Overlayable::PRODUCT_SERVICES:
+ overlayable.policies |= Overlayable::Policy::kProductServices;
+ break;
+ default:
+ *out_error = "unknown overlayable policy";
+ return false;
+ }
}
if (pb_overlayable.has_source()) {
DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
}
+
overlayable.comment = pb_overlayable.comment();
- entry->overlayable_declarations.push_back(overlayable);
+ entry->overlayable = overlayable;
}
ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index ecf34d1..70bf868 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -310,26 +310,24 @@
pb_allow_new->set_comment(entry->allow_new.value().comment);
}
- for (const Overlayable& overlayable : entry->overlayable_declarations) {
- pb::Overlayable* pb_overlayable = pb_entry->add_overlayable();
- if (overlayable.policy) {
- switch (overlayable.policy.value()) {
- case Overlayable::Policy::kPublic:
- pb_overlayable->set_policy(pb::Overlayable::PUBLIC);
- break;
- case Overlayable::Policy::kProduct:
- pb_overlayable->set_policy(pb::Overlayable::PRODUCT);
- break;
- case Overlayable::Policy::kProductServices:
- pb_overlayable->set_policy(pb::Overlayable::PRODUCT_SERVICES);
- break;
- case Overlayable::Policy::kSystem:
- pb_overlayable->set_policy(pb::Overlayable::SYSTEM);
- break;
- case Overlayable::Policy::kVendor:
- pb_overlayable->set_policy(pb::Overlayable::VENDOR);
- break;
- }
+ if (entry->overlayable) {
+ pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
+
+ Overlayable overlayable = entry->overlayable.value();
+ if (overlayable.policies & Overlayable::Policy::kPublic) {
+ pb_overlayable->add_policy(pb::Overlayable::PUBLIC);
+ }
+ if (overlayable.policies & Overlayable::Policy::kProduct) {
+ pb_overlayable->add_policy(pb::Overlayable::PRODUCT);
+ }
+ if (overlayable.policies & Overlayable::Policy::kProductServices) {
+ pb_overlayable->add_policy(pb::Overlayable::PRODUCT_SERVICES);
+ }
+ if (overlayable.policies & Overlayable::Policy::kSystem) {
+ pb_overlayable->add_policy(pb::Overlayable::SYSTEM);
+ }
+ if (overlayable.policies & Overlayable::Policy::kVendor) {
+ pb_overlayable->add_policy(pb::Overlayable::VENDOR);
}
SerializeSourceToPb(overlayable.source, &source_pool,
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 1cd2f0b..fb913f40 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,7 +93,7 @@
util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
// Make an overlayable resource.
- ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
+ ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
Overlayable{}, test::GetDiagnostics()));
pb::ResourceTable pb_table;
@@ -160,8 +160,9 @@
new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
+ Eq(Overlayable::Policy::kNone));
}
TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -502,15 +503,26 @@
}
TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
+ Overlayable overlayable_foo{};
+ overlayable_foo.policies |= Overlayable::Policy::kSystem;
+ overlayable_foo.policies |= Overlayable::Policy::kProduct;
+
+ Overlayable overlayable_bar{};
+ overlayable_bar.policies |= Overlayable::Policy::kProductServices;
+ overlayable_bar.policies |= Overlayable::Policy::kVendor;
+
+ Overlayable overlayable_baz{};
+ overlayable_baz.policies |= Overlayable::Policy::kPublic;
+
+ Overlayable overlayable_biz{};
+
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kSystem)
- .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kProduct)
- .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kProductServices)
- .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kVendor)
- .AddOverlayable("com.app.a:bool/baz", Overlayable::Policy::kPublic)
- .AddOverlayable("com.app.a:bool/biz", {})
+ .SetOverlayable("com.app.a:bool/foo", overlayable_foo)
+ .SetOverlayable("com.app.a:bool/bar", overlayable_bar)
+ .SetOverlayable("com.app.a:bool/baz", overlayable_baz)
+ .SetOverlayable("com.app.a:bool/biz", overlayable_biz)
.AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
.Build();
@@ -523,37 +535,36 @@
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
EXPECT_THAT(error, IsEmpty());
- Maybe<ResourceTable::SearchResult> result =
+ Maybe<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kSystem));
- EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kSystem
+ | Overlayable::Policy::kProduct));
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProductServices));
- EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kVendor));
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProductServices
+ | Overlayable::Policy::kVendor));
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kPublic));
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kPublic);
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(result.value().entry->overlayable_declarations[0].policy);
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kNone);
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
- ASSERT_TRUE(result);
- EXPECT_THAT(result.value().entry->overlayable_declarations.size(), Eq(0));
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+ ASSERT_TRUE(search_result);
+ ASSERT_FALSE(search_result.value().entry->overlayable);
}
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 3a5d585..8cbc037 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -368,7 +368,16 @@
// Symbol state information may be lost if there is no value for the resource.
if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
- << "no definition for declared symbol '" << name << "'");
+ << "no definition for declared symbol '" << name
+ << "'");
+ error = true;
+ }
+
+ // Ensure that definitions for values declared as overlayable exist
+ if (entry->overlayable && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->overlayable.value().source)
+ << "no definition for overlayable symbol '"
+ << name << "'");
error = true;
}
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index d777e22..22e1723 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -134,35 +134,21 @@
dst_entry->allow_new = std::move(src_entry->allow_new);
}
- for (auto& src_overlayable : src_entry->overlayable_declarations) {
- for (auto& dst_overlayable : dst_entry->overlayable_declarations) {
- // An overlayable resource cannot be declared twice with the same policy
- if (src_overlayable.policy == dst_overlayable.policy) {
- context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
- << "duplicate overlayable declaration for resource '"
- << src_entry->name << "'");
- context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
- << "previous declaration here");
- return false;
- }
-
- // An overlayable resource cannot be declared once with a policy and without a policy because
- // the policy becomes unused
- if (!src_overlayable.policy || !dst_overlayable.policy) {
- context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
- << "overlayable resource '" << src_entry->name
- << "' declared once with a policy and once with no "
- << "policy");
- context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
- << "previous declaration here");
- return false;
- }
+ if (src_entry->overlayable) {
+ if (dst_entry->overlayable) {
+ // Do not allow a resource with an overlayable declaration to have that overlayable
+ // declaration redefined
+ context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+ << "duplicate overlayable declaration for resource '"
+ << src_entry->name << "'");
+ context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+ << "previous declaration here");
+ return false;
+ } else {
+ dst_entry->overlayable = std::move(src_entry->overlayable);
}
}
- dst_entry->overlayable_declarations.insert(dst_entry->overlayable_declarations.end(),
- src_entry->overlayable_declarations.begin(),
- src_entry->overlayable_declarations.end());
return true;
}
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index d6579d3..17b2a83 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -436,17 +436,21 @@
Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
}
-TEST_F(TableMergerTest, AddOverlayable) {
+TEST_F(TableMergerTest, SetOverlayable) {
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ overlayable.policies |= Overlayable::Policy::kVendor;
+
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+ .SetOverlayable("bool/foo", overlayable)
.Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProductServices)
+ .AddSimple("bool/foo")
.Build();
ResourceTable final_table;
@@ -457,26 +461,61 @@
ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
- Maybe<ResourceTable::SearchResult> result = final_table.FindResource(name);
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
- ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProductServices));
+ Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
+ | Overlayable::Policy::kVendor));
}
-TEST_F(TableMergerTest, AddDuplicateOverlayableFail) {
+TEST_F(TableMergerTest, SetOverlayableLater) {
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+ .AddSimple("bool/foo")
.Build();
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kPublic;
+ overlayable.policies |= Overlayable::Policy::kProductServices;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+ .SetOverlayable("bool/foo", overlayable)
+ .Build();
+
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = true;
+ TableMerger merger(context_.get(), &final_table, options);
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
+
+ const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
+ Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kPublic
+ | Overlayable::Policy::kProductServices));
+}
+
+TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
+ Overlayable overlayable_first{};
+ overlayable_first.policies |= Overlayable::Policy::kProduct;
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .SetOverlayable("bool/foo", overlayable_first)
+ .Build();
+
+ Overlayable overlayable_second{};
+ overlayable_second.policies |= Overlayable::Policy::kProduct;
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .SetOverlayable("bool/foo", overlayable_second)
.Build();
ResourceTable final_table;
@@ -487,38 +526,21 @@
ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
}
-TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneFirstFail) {
+TEST_F(TableMergerTest, SetOverlayableDifferentPolicesFail) {
+ Overlayable overlayable_first{};
+ overlayable_first.policies |= Overlayable::Policy::kVendor;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", {})
+ .SetOverlayable("bool/foo",overlayable_first)
.Build();
+ Overlayable overlayable_second{};
+ overlayable_second.policies |= Overlayable::Policy::kProduct;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
- .Build();
-
- ResourceTable final_table;
- TableMergerOptions options;
- options.auto_add_overlay = true;
- TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
- ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
-}
-
-TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneLastFail) {
- std::unique_ptr<ResourceTable> table_a =
- test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
- .Build();
-
- std::unique_ptr<ResourceTable> table_b =
- test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", {})
+ .SetOverlayable("bool/foo", overlayable_second)
.Build();
ResourceTable final_table;
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 2e717ff..9c5b5d3 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,7 +248,7 @@
if (!split_entry->id) {
split_entry->id = entry->id;
split_entry->visibility = entry->visibility;
- split_entry->overlayable_declarations = entry->overlayable_declarations;
+ split_entry->overlayable = entry->overlayable;
}
// Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 03b59e0..884ec38 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -135,12 +135,11 @@
return *this;
}
-ResourceTableBuilder& ResourceTableBuilder::AddOverlayable(const StringPiece& name,
- const Maybe<Overlayable::Policy> p) {
+ResourceTableBuilder& ResourceTableBuilder::SetOverlayable(const StringPiece& name,
+ const Overlayable& overlayable) {
+
ResourceName res_name = ParseNameOrDie(name);
- Overlayable overlayable;
- overlayable.policy = p;
- CHECK(table_->AddOverlayable(res_name, overlayable, GetDiagnostics()));
+ CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
return *this;
}
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index d68c24d..a120484 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -73,8 +73,8 @@
const ResourceId& id, std::unique_ptr<Value> value);
ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
Visibility::Level level, bool allow_new = false);
- ResourceTableBuilder& AddOverlayable(const android::StringPiece& name,
- Maybe<Overlayable::Policy> policy);
+ ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
+ const Overlayable& overlayable);
StringPool* string_pool();
std::unique_ptr<ResourceTable> Build();
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 2291e5a..4ffcf68 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -11,6 +11,7 @@
android.system \
com.android.bouncycastle \
com.android.conscrypt \
+ com.android.i18n.phonenumbers \
com.android.okhttp \
com.sun \
dalvik \
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 9dceba2..6b4c346 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -155,7 +155,7 @@
print
print 'std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({'
for locale in sorted(representative_locales):
- print ' 0x%08Xllu, // %s' % (
+ print ' 0x%08XLLU, // %s' % (
pack_to_uint64(locale),
locale)
print '});'
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
new file mode 100644
index 0000000..ca6b3c4
--- /dev/null
+++ b/tools/processors/view_inspector/Android.bp
@@ -0,0 +1,27 @@
+java_library_host {
+ name: "view-inspector-annotation-processor",
+
+ srcs: ["src/java/**/*.java"],
+ java_resource_dirs: ["src/resources"],
+
+ static_libs: [
+ "javapoet",
+ ],
+
+ use_tools_jar: true,
+}
+
+java_test_host {
+ name: "view-inspector-annotation-processor-test",
+
+ srcs: ["test/java/**/*.java"],
+ java_resource_dirs: ["test/resources"],
+
+ static_libs: [
+ "guava",
+ "junit",
+ "view-inspector-annotation-processor",
+ ],
+
+ test_suites: ["general-tests"],
+}
diff --git a/tools/processors/view_inspector/OWNERS b/tools/processors/view_inspector/OWNERS
new file mode 100644
index 0000000..0473f54
--- /dev/null
+++ b/tools/processors/view_inspector/OWNERS
@@ -0,0 +1,3 @@
+alanv@google.com
+ashleyrose@google.com
+aurimas@google.com
\ No newline at end of file
diff --git a/tools/processors/view_inspector/TEST_MAPPING b/tools/processors/view_inspector/TEST_MAPPING
new file mode 100644
index 0000000..a91b5b4
--- /dev/null
+++ b/tools/processors/view_inspector/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "view-inspector-annotation-processor-test"
+ }
+ ]
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
new file mode 100644
index 0000000..f157949
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import java.util.Map;
+import java.util.Optional;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * Utilities for working with {@link AnnotationMirror}.
+ */
+final class AnnotationUtils {
+ private final Elements mElementUtils;
+ private final Types mTypeUtils;
+
+ AnnotationUtils(ProcessingEnvironment processingEnv) {
+ mElementUtils = processingEnv.getElementUtils();
+ mTypeUtils = processingEnv.getTypeUtils();
+ }
+
+ /**
+ * Get a {@link AnnotationMirror} specified by name from an {@link Element}.
+ *
+ * @param qualifiedName The fully qualified name of the annotation to search for
+ * @param element The element to search for annotations on
+ * @return The mirror of the requested annotation
+ * @throws ProcessingException If there is not exactly one of the requested annotation.
+ */
+ AnnotationMirror exactlyOneMirror(String qualifiedName, Element element) {
+ final Element targetTypeElment = mElementUtils.getTypeElement(qualifiedName);
+ final TypeMirror targetType = targetTypeElment.asType();
+ AnnotationMirror result = null;
+
+ for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
+ final TypeMirror annotationType = annotation.getAnnotationType().asElement().asType();
+ if (mTypeUtils.isSameType(annotationType, targetType)) {
+ if (result == null) {
+ result = annotation;
+ } else {
+ final String message = String.format(
+ "Element had multiple instances of @%s, expected exactly one",
+ targetTypeElment.getSimpleName());
+
+ throw new ProcessingException(message, element, annotation);
+ }
+ }
+ }
+
+ if (result == null) {
+ final String message = String.format(
+ "Expected an @%s annotation, found none", targetTypeElment.getSimpleName());
+ throw new ProcessingException(message, element);
+ } else {
+ return result;
+ }
+ }
+
+ /**
+ * Extract a string-valued property from an {@link AnnotationMirror}.
+ *
+ * @param propertyName The name of the requested property
+ * @param annotationMirror The mirror to search for the property
+ * @return The String value of the annotation or null
+ */
+ Optional<String> stringProperty(String propertyName, AnnotationMirror annotationMirror) {
+ final AnnotationValue value = valueByName(propertyName, annotationMirror);
+ if (value != null) {
+ return Optional.of((String) value.getValue());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+
+ /**
+ * Extract a {@link AnnotationValue} from a mirror by string property name.
+ *
+ * @param propertyName The name of the property requested property
+ * @param annotationMirror
+ * @return
+ */
+ AnnotationValue valueByName(String propertyName, AnnotationMirror annotationMirror) {
+ final Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap =
+ annotationMirror.getElementValues();
+
+ for (ExecutableElement method : valueMap.keySet()) {
+ if (method.getSimpleName().contentEquals(propertyName)) {
+ return valueMap.get(method);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
new file mode 100644
index 0000000..579745d
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import com.squareup.javapoet.ClassName;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Model of an inspectable class derived from annotations.
+ *
+ * This class does not use any {@code javax.lang.model} objects to facilitate building models for
+ * testing {@link InspectionCompanionGenerator}.
+ */
+public final class InspectableClassModel {
+ private final ClassName mClassName;
+ private final Map<String, Property> mPropertyMap;
+ private Optional<String> mNodeName = Optional.empty();
+
+ /**
+ * @param className The name of the modeled class
+ */
+ public InspectableClassModel(ClassName className) {
+ mClassName = className;
+ mPropertyMap = new HashMap<>();
+ }
+
+ public ClassName getClassName() {
+ return mClassName;
+ }
+
+ public Optional<String> getNodeName() {
+ return mNodeName;
+ }
+
+ public void setNodeName(Optional<String> nodeName) {
+ mNodeName = nodeName;
+ }
+
+ /**
+ * Add a property to the model, replacing an existing property of the same name.
+ *
+ * @param property The property to add or replace
+ */
+ public void putProperty(Property property) {
+ mPropertyMap.put(property.getName(), property);
+ }
+
+ /**
+ * Get a property by name.
+ *
+ * @param name The name of the property
+ * @return The property or an empty optional
+ */
+ public Optional<Property> getProperty(String name) {
+ return Optional.of(mPropertyMap.get(name));
+ }
+
+ /**
+ * Get all the properties defined on this model.
+ *
+ * @return An un-ordered collection of properties
+ */
+ public Collection<Property> getAllProperties() {
+ return mPropertyMap.values();
+ }
+
+ /**
+ * Model an inspectable property
+ */
+ public static final class Property {
+ private final String mName;
+ private String mGetter;
+ private Type mType;
+ private boolean mAttributeIdInferrableFromR = true;
+ private int mAttributeId = 0;
+
+ public Property(String name) {
+ mName = name;
+ }
+
+ public int getAttributeId() {
+ return mAttributeId;
+ }
+
+ /**
+ * Set the attribute ID, and mark the attribute ID as non-inferrable.
+ *
+ * @param attributeId The attribute ID for this property
+ */
+ public void setAttributeId(int attributeId) {
+ mAttributeIdInferrableFromR = false;
+ mAttributeId = attributeId;
+ }
+
+ public boolean isAttributeIdInferrableFromR() {
+ return mAttributeIdInferrableFromR;
+ }
+
+ public void setAttributeIdInferrableFromR(boolean attributeIdInferrableFromR) {
+ mAttributeIdInferrableFromR = attributeIdInferrableFromR;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getGetter() {
+ return mGetter;
+ }
+
+ public void setGetter(String getter) {
+ mGetter = getter;
+ }
+
+ public Type getType() {
+ return mType;
+ }
+
+ public void setType(Type type) {
+ mType = type;
+ }
+
+ public enum Type {
+ /** Primitive or boxed {@code boolean} */
+ BOOLEAN,
+
+ /** Primitive or boxed {@code byte} */
+ BYTE,
+
+ /** Primitive or boxed {@code char} */
+ CHAR,
+
+ /** Primitive or boxed {@code double} */
+ DOUBLE,
+
+ /** Primitive or boxed {@code float} */
+ FLOAT,
+
+ /** Primitive or boxed {@code int} */
+ INT,
+
+ /** Primitive or boxed {@code long} */
+ LONG,
+
+ /** Primitive or boxed {@code short} */
+ SHORT,
+
+ /** Any other object */
+ OBJECT,
+
+ /**
+ * A color object or packed color {@code int} or {@code long}.
+ *
+ * @see android.graphics.Color
+ * @see android.annotation.ColorInt
+ * @see android.annotation.ColorLong
+ */
+ COLOR,
+
+ /**
+ * An {@code int} packed with a gravity specification
+ *
+ * @see android.view.Gravity
+ */
+ GRAVITY,
+
+ /**
+ * An enumeration packed into an {@code int}.
+ *
+ * @see android.view.inspector.IntEnumMapping
+ */
+ INT_ENUM,
+
+ /**
+ * Non-exclusive or partially-exclusive flags packed into an {@code int}.
+ *
+ * @see android.view.inspector.IntFlagMapping
+ */
+ INT_FLAG
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
new file mode 100644
index 0000000..a186a82
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import java.util.Optional;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+/**
+ * Process {InspectableNodeName} annotations
+ *
+ * @see android.view.inspector.InspectableNodeName
+ */
+public final class InspectableNodeNameProcessor implements ModelProcessor {
+ private final String mQualifiedName;
+ private final ProcessingEnvironment mProcessingEnv;
+ private final AnnotationUtils mAnnotationUtils;
+
+ /**
+ * @param annotationQualifiedName The qualified name of the annotation to process
+ * @param processingEnv The processing environment from the parent processor
+ */
+ public InspectableNodeNameProcessor(
+ String annotationQualifiedName,
+ ProcessingEnvironment processingEnv) {
+ mQualifiedName = annotationQualifiedName;
+ mProcessingEnv = processingEnv;
+ mAnnotationUtils = new AnnotationUtils(processingEnv);
+ }
+
+ /**
+ * Set the node name on the model if one is supplied.
+ *
+ * If the model already has a different node name, the node name will not be updated, and
+ * the processor will print an error the the messager.
+ *
+ * @param element The annotated element to operate on
+ * @param model The model this element should be merged into
+ */
+ @Override
+ public void process(Element element, InspectableClassModel model) {
+ try {
+ final AnnotationMirror mirror =
+ mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
+ final Optional<String> nodeName = mAnnotationUtils.stringProperty("value", mirror);
+
+ if (!model.getNodeName().isPresent() || model.getNodeName().equals(nodeName)) {
+ model.setNodeName(nodeName);
+ } else {
+ final String message = String.format(
+ "Node name was already set to \"%s\", refusing to change it to \"%s\".",
+ model.getNodeName().get(),
+ nodeName);
+ throw new ProcessingException(message, element, mirror);
+ }
+ } catch (ProcessingException processingException) {
+ processingException.print(mProcessingEnv.getMessager());
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
new file mode 100644
index 0000000..3b85dbb
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import android.processor.view.inspector.InspectableClassModel.Property;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.NameAllocator;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.Modifier;
+
+/**
+ * Generates a source file defining a {@link android.view.inspector.InspectionCompanion}.
+ */
+public final class InspectionCompanionGenerator {
+ private final Filer mFiler;
+ private final Class mRequestingClass;
+
+ /**
+ * The class name for {@code R.java}.
+ */
+ private static final ClassName R_CLASS_NAME = ClassName.get("android", "R");
+
+ /**
+ * The class name of {@link android.content.res.ResourceId}.
+ */
+ private static final ClassName RESOURCE_ID_CLASS_NAME = ClassName.get(
+ "android.content.res", "ResourceId");
+
+ /**
+ * The class name of {@link android.view.inspector.InspectionCompanion}.
+ */
+ private static final ClassName INSPECTION_COMPANION = ClassName.get(
+ "android.view.inspector", "InspectionCompanion");
+
+ /**
+ * The class name of {@link android.view.inspector.PropertyMapper}.
+ */
+ private static final ClassName PROPERTY_MAPPER = ClassName.get(
+ "android.view.inspector", "PropertyMapper");
+
+ /**
+ * The class name of {@link android.view.inspector.PropertyReader}.
+ */
+ private static final ClassName PROPERTY_READER = ClassName.get(
+ "android.view.inspector", "PropertyReader");
+
+ /**
+ * The {@code mPropertiesMapped} field.
+ */
+ private static final FieldSpec M_PROPERTIES_MAPPED = FieldSpec
+ .builder(TypeName.BOOLEAN, "mPropertiesMapped", Modifier.PRIVATE)
+ .initializer("false")
+ .addJavadoc(
+ "Set by {@link #mapProperties($T)} once properties have been mapped.\n",
+ PROPERTY_MAPPER)
+ .build();
+
+ /**
+ * The suffix of the generated class name after the class's binary name.
+ */
+ private static final String GENERATED_CLASS_SUFFIX = "$$InspectionCompanion";
+
+ /**
+ * The null resource ID.
+ *
+ * @see android.content.res.ResourceId#ID_NULL
+ */
+ private static final int NO_ID = 0;
+
+ /**
+ * @param filer A filer to write the generated source to
+ * @param requestingClass A class object representing the class that invoked the generator
+ */
+ public InspectionCompanionGenerator(Filer filer, Class requestingClass) {
+ mFiler = filer;
+ mRequestingClass = requestingClass;
+ }
+
+ /**
+ * Generate and write an inspection companion.
+ *
+ * @param model The model to generated
+ * @throws IOException From the Filer
+ */
+ public void generate(InspectableClassModel model) throws IOException {
+ generateFile(model).writeTo(mFiler);
+ }
+
+ /**
+ * Generate a {@link JavaFile} from a model.
+ *
+ * This is package-public for testing.
+ *
+ * @param model The model to generate from
+ * @return A generated file of an {@link android.view.inspector.InspectionCompanion}
+ */
+ JavaFile generateFile(InspectableClassModel model) {
+ return JavaFile
+ .builder(model.getClassName().packageName(), generateTypeSpec(model))
+ .indent(" ")
+ .build();
+ }
+
+ /**
+ * Generate a {@link TypeSpec} for the {@link android.view.inspector.InspectionCompanion}
+ * for the supplied model.
+ *
+ * @param model The model to generate from
+ * @return A TypeSpec of the inspection companion
+ */
+ private TypeSpec generateTypeSpec(InspectableClassModel model) {
+ final List<PropertyIdField> propertyIdFields = generatePropertyIdFields(model);
+
+ TypeSpec.Builder builder = TypeSpec
+ .classBuilder(generateClassName(model))
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addSuperinterface(ParameterizedTypeName.get(
+ INSPECTION_COMPANION, model.getClassName()))
+ .addJavadoc("Inspection companion for {@link $T}.\n\n", model.getClassName())
+ .addJavadoc("Generated by {@link $T}\n", getClass())
+ .addJavadoc("on behalf of {@link $T}.\n", mRequestingClass)
+ .addField(M_PROPERTIES_MAPPED);
+
+ for (PropertyIdField propertyIdField : propertyIdFields) {
+ builder.addField(propertyIdField.mFieldSpec);
+ }
+
+ builder.addMethod(generateMapProperties(propertyIdFields))
+ .addMethod(generateReadProperties(model, propertyIdFields));
+
+ generateGetNodeName(model).ifPresent(builder::addMethod);
+
+ return builder.build();
+ }
+
+ /**
+ * Build a list of {@link PropertyIdField}'s for a model.
+ *
+ * To insure idempotency of the generated code, this method sorts the list of properties
+ * alphabetically by name.
+ *
+ * A {@link NameAllocator} is used to ensure that the field names are valid Java identifiers,
+ * and it prevents overlaps in names by suffixing them as needed.
+ *
+ * @param model The model to get properties from
+ * @return A list of properties and fields
+ */
+ private List<PropertyIdField> generatePropertyIdFields(InspectableClassModel model) {
+ final NameAllocator nameAllocator = new NameAllocator();
+ final List<Property> sortedProperties = new ArrayList<>(model.getAllProperties());
+ final List<PropertyIdField> propertyIdFields = new ArrayList<>(sortedProperties.size());
+
+ sortedProperties.sort(Comparator.comparing(Property::getName));
+
+ for (Property property : sortedProperties) {
+ // Format a property to a member field name like "someProperty" -> "mSomePropertyId"
+ final String memberName = String.format(
+ "m%s%sId",
+ property.getName().substring(0, 1).toUpperCase(),
+ property.getName().substring(1));
+ final FieldSpec fieldSpec = FieldSpec
+ .builder(TypeName.INT, nameAllocator.newName(memberName), Modifier.PRIVATE)
+ .addJavadoc("Property ID of {@code $L}.\n", property.getName())
+ .build();
+
+ propertyIdFields.add(new PropertyIdField(fieldSpec, property));
+ }
+
+ return propertyIdFields;
+ }
+
+ /**
+ * Generate a method definition for
+ * {@link android.view.inspector.InspectionCompanion#getNodeName()}, if needed.
+ *
+ * If {@link InspectableClassModel#getNodeName()} is empty, This method returns an empty
+ * optional, otherwise, it generates a simple method that returns the string value of the
+ * node name.
+ *
+ * @param model The model to generate from
+ * @return The method definition or an empty Optional
+ */
+ private Optional<MethodSpec> generateGetNodeName(InspectableClassModel model) {
+ return model.getNodeName().map(nodeName -> MethodSpec.methodBuilder("getNodeName")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(String.class)
+ .addStatement("return $S", nodeName)
+ .build());
+ }
+
+ /**
+ * Generate a method definition for
+ * {@link android.view.inspector.InspectionCompanion#mapProperties(
+ * android.view.inspector.PropertyMapper)}.
+ *
+ * @param propertyIdFields A list of properties to map to ID fields
+ * @return The method definition
+ */
+ private MethodSpec generateMapProperties(List<PropertyIdField> propertyIdFields) {
+ final MethodSpec.Builder builder = MethodSpec.methodBuilder("mapProperties")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(PROPERTY_MAPPER, "propertyMapper");
+
+ propertyIdFields.forEach(p -> builder.addStatement(generatePropertyMapperInvocation(p)));
+ builder.addStatement("$N = true", M_PROPERTIES_MAPPED);
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a method definition for
+ * {@link android.view.inspector.InspectionCompanion#readProperties(
+ * Object, android.view.inspector.PropertyReader)}.
+ *
+ * @param model The model to generate from
+ * @param propertyIdFields A list of properties and ID fields to read from
+ * @return The method definition
+ */
+ private MethodSpec generateReadProperties(
+ InspectableClassModel model,
+ List<PropertyIdField> propertyIdFields) {
+ final MethodSpec.Builder builder = MethodSpec.methodBuilder("readProperties")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(model.getClassName(), "inspectable")
+ .addParameter(PROPERTY_READER, "propertyReader")
+ .addCode(generatePropertyMapInitializationCheck());
+
+ for (PropertyIdField propertyIdField : propertyIdFields) {
+ builder.addStatement(
+ "propertyReader.read$L($N, inspectable.$L())",
+ methodSuffixForPropertyType(propertyIdField.mProperty.getType()),
+ propertyIdField.mFieldSpec,
+ propertyIdField.mProperty.getGetter());
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a statement maps a property with a {@link android.view.inspector.PropertyMapper}.
+ *
+ * @param propertyIdField The property model and ID field to generate from
+ * @return A statement that invokes property mapper method
+ */
+ private CodeBlock generatePropertyMapperInvocation(PropertyIdField propertyIdField) {
+ final CodeBlock.Builder builder = CodeBlock.builder();
+ final Property property = propertyIdField.mProperty;
+ final FieldSpec fieldSpec = propertyIdField.mFieldSpec;
+
+ builder.add(
+ "$N = propertyMapper.map$L($S,$W",
+ fieldSpec,
+ methodSuffixForPropertyType(property.getType()),
+ property.getName());
+
+ if (property.isAttributeIdInferrableFromR()) {
+ builder.add("$T.attr.$L", R_CLASS_NAME, property.getName());
+ } else {
+ if (property.getAttributeId() == NO_ID) {
+ builder.add("$T.ID_NULL", RESOURCE_ID_CLASS_NAME);
+ } else {
+ builder.add("$L", String.format("0x%08x", property.getAttributeId()));
+ }
+ }
+
+ switch (property.getType()) {
+ case INT_ENUM:
+ throw new RuntimeException("IntEnumMapping generation not implemented");
+ case INT_FLAG:
+ throw new RuntimeException("IntFlagMapping generation not implemented");
+ default:
+ builder.add(")");
+ break;
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a check that throws
+ * {@link android.view.inspector.InspectionCompanion.UninitializedPropertyMapException}
+ * if the properties haven't been initialized.
+ *
+ * <pre>
+ * if (!mPropertiesMapped) {
+ * throw new InspectionCompanion.UninitializedPropertyMapException();
+ * }
+ * </pre>
+ *
+ * @return A codeblock containing the property map initialization check
+ */
+ private CodeBlock generatePropertyMapInitializationCheck() {
+ return CodeBlock.builder()
+ .beginControlFlow("if (!$N)", M_PROPERTIES_MAPPED)
+ .addStatement(
+ "throw new $T()",
+ INSPECTION_COMPANION.nestedClass("UninitializedPropertyMapException"))
+ .endControlFlow()
+ .build();
+ }
+
+ /**
+ * Generate the final class name for the inspection companion from the model's class name.
+ *
+ * The generated class is added to the same package as the source class. If the class in the
+ * model is a nested class, the nested class names are joined with {@code "$"}. The suffix
+ * {@code "$$InspectionCompanion"} is always added the the generated name. E.g.: For modeled
+ * class {@code com.example.Outer.Inner}, the generated class name will be
+ * {@code com.example.Outer$Inner$$InspectionCompanion}.
+ *
+ * @param model The model to generate from
+ * @return A class name for the generated inspection companion class
+ */
+ private static ClassName generateClassName(InspectableClassModel model) {
+ final ClassName className = model.getClassName();
+
+ return ClassName.get(
+ className.packageName(),
+ String.join("$", className.simpleNames()) + GENERATED_CLASS_SUFFIX);
+ }
+
+ /**
+ * Get the suffix for a {@code map} or {@code read} method for a property type.
+ *
+ * @param type The requested property type
+ * @return A method suffix
+ */
+ private static String methodSuffixForPropertyType(Property.Type type) {
+ switch (type) {
+ case BOOLEAN:
+ return "Boolean";
+ case BYTE:
+ return "Byte";
+ case CHAR:
+ return "Char";
+ case DOUBLE:
+ return "Double";
+ case FLOAT:
+ return "Float";
+ case INT:
+ return "Int";
+ case LONG:
+ return "Long";
+ case SHORT:
+ return "Short";
+ case OBJECT:
+ return "Object";
+ case COLOR:
+ return "Color";
+ case GRAVITY:
+ return "Gravity";
+ case INT_ENUM:
+ return "IntEnum";
+ case INT_FLAG:
+ return "IntFlag";
+ default:
+ throw new NoSuchElementException(String.format("No such property type, %s", type));
+ }
+ }
+
+ /**
+ * Value class that holds a {@link Property} and a {@link FieldSpec} for that property.
+ */
+ private static final class PropertyIdField {
+ private final FieldSpec mFieldSpec;
+ private final Property mProperty;
+
+ private PropertyIdField(FieldSpec fieldSpec, Property property) {
+ mFieldSpec = fieldSpec;
+ mProperty = property;
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
new file mode 100644
index 0000000..3ffcff8
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import javax.lang.model.element.Element;
+
+/**
+ * An interface for annotation processors that operate on a single element and a class model.
+ */
+public interface ModelProcessor {
+ /**
+ * Process the supplied element, mutating the model as needed.
+ *
+ * @param element The annotated element to operate on
+ * @param model The model this element should be merged into
+ */
+ void process(Element element, InspectableClassModel model);
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
new file mode 100644
index 0000000..e531b67
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.squareup.javapoet.ClassName;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+
+
+/**
+ * An annotation processor for the platform inspectable annotations.
+ *
+ * It mostly delegates to {@link ModelProcessor} and {@link InspectionCompanionGenerator}. This
+ * modular architecture allows the core generation code to be reused for comparable annotations
+ * outside the platform, such as in AndroidX.
+ *
+ * @see android.view.inspector.InspectableNodeName
+ * @see android.view.inspector.InspectableProperty
+ */
+@SupportedAnnotationTypes({
+ PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME
+})
+public final class PlatformInspectableProcessor extends AbstractProcessor {
+ static final String NODE_NAME_QUALIFIED_NAME =
+ "android.view.inspector.InspectableNodeName";
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ final Map<String, InspectableClassModel> modelMap = new HashMap<>();
+
+ for (TypeElement annotation : annotations) {
+ if (annotation.getQualifiedName().contentEquals(NODE_NAME_QUALIFIED_NAME)) {
+ runModelProcessor(
+ roundEnv.getElementsAnnotatedWith(annotation),
+ new InspectableNodeNameProcessor(NODE_NAME_QUALIFIED_NAME, processingEnv),
+ modelMap);
+
+
+ } else {
+ fail("Unexpected annotation type", annotation);
+ }
+ }
+
+ final InspectionCompanionGenerator generator =
+ new InspectionCompanionGenerator(processingEnv.getFiler(), getClass());
+
+ for (InspectableClassModel model : modelMap.values()) {
+ try {
+ generator.generate(model);
+ } catch (IOException ioException) {
+ fail(String.format(
+ "Unable to generate inspection companion for %s due to %s",
+ model.getClassName().toString(),
+ ioException.getMessage()));
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Run a {@link ModelProcessor} for a set of elements
+ *
+ * @param elements Elements to process, should be annotated correctly
+ * @param processor The processor to use
+ * @param modelMap A map of qualified class names to models
+ */
+ private void runModelProcessor(
+ Set<? extends Element> elements,
+ ModelProcessor processor,
+ Map<String, InspectableClassModel> modelMap) {
+ for (Element element : elements) {
+ final Optional<TypeElement> classElement = enclosingClassElement(element);
+
+ if (!classElement.isPresent()) {
+ fail("Element not contained in a class", element);
+ break;
+ }
+
+ final InspectableClassModel model = modelMap.computeIfAbsent(
+ classElement.get().getQualifiedName().toString(),
+ k -> new InspectableClassModel(ClassName.get(classElement.get())));
+
+ processor.process(element, model);
+ }
+ }
+
+ /**
+ * Get the nearest enclosing class if there is one.
+ *
+ * If {@param element} represents a class, it will be returned wrapped in an optional.
+ *
+ * @param element An element to search from
+ * @return A TypeElement of the nearest enclosing class or an empty optional
+ */
+ private static Optional<TypeElement> enclosingClassElement(Element element) {
+ Element cursor = element;
+
+ while (cursor != null) {
+ if (cursor.getKind() == ElementKind.CLASS) {
+ return Optional.of((TypeElement) cursor);
+ }
+
+ cursor = cursor.getEnclosingElement();
+ }
+
+ return Optional.empty();
+ }
+
+ /**
+ * Print message and fail the build.
+ *
+ * @param message Message to print
+ */
+ private void fail(String message) {
+ processingEnv.getMessager().printMessage(ERROR, message);
+ }
+
+ /**
+ * Print message and fail the build.
+ *
+ * @param message Message to print
+ * @param element The element that failed
+ */
+ private void fail(String message, Element element) {
+ processingEnv.getMessager().printMessage(ERROR, message, element);
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
new file mode 100644
index 0000000..6360e0a
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+
+/**
+ * Internal exception used to signal an error processing an annotation.
+ */
+final class ProcessingException extends RuntimeException {
+ private final Element mElement;
+ private final AnnotationMirror mAnnotationMirror;
+ private final AnnotationValue mAnnotationValue;
+
+ ProcessingException(String message) {
+ this(message, null, null, null);
+ }
+
+ ProcessingException(String message, Element element) {
+ this(message, element, null, null);
+ }
+
+ ProcessingException(String message, Element element, AnnotationMirror annotationMirror) {
+ this(message, element, annotationMirror, null);
+ }
+
+ ProcessingException(
+ String message,
+ Element element,
+ AnnotationMirror annotationMirror,
+ AnnotationValue annotationValue) {
+ super(message);
+ mElement = element;
+ mAnnotationMirror = annotationMirror;
+ mAnnotationValue = annotationValue;
+ }
+
+ /**
+ * Prints the exception to a Messager.
+ *
+ * @param messager A Messager to print to
+ */
+ void print(Messager messager) {
+ if (mElement != null) {
+ if (mAnnotationMirror != null) {
+ if (mAnnotationValue != null) {
+ messager.printMessage(
+ ERROR, getMessage(), mElement, mAnnotationMirror, mAnnotationValue);
+ } else {
+ messager.printMessage(ERROR, getMessage(), mElement, mAnnotationMirror);
+ }
+ } else {
+ messager.printMessage(ERROR, getMessage(), mElement);
+ }
+ } else {
+ messager.printMessage(ERROR, getMessage());
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..fa4f71f
--- /dev/null
+++ b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+android.processor.inspector.view.PlatformInspectableProcessor
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
new file mode 100644
index 0000000..f639719
--- /dev/null
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import android.processor.view.inspector.InspectableClassModel.Property;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.fail;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import com.squareup.javapoet.ClassName;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Optional;
+
+/**
+ * Tests for {@link InspectionCompanionGenerator}
+ */
+public class InspectionCompanionGeneratorTest {
+ private static final String RESOURCE_PATH_TEMPLATE =
+ "android/processor/view/inspector/InspectionCompanionGeneratorTest/%s.java.txt";
+ private static final ClassName TEST_CLASS_NAME =
+ ClassName.get("com.android.inspectable", "TestInspectable");
+ private InspectableClassModel mModel;
+ private InspectionCompanionGenerator mGenerator;
+
+ @Before
+ public void setup() {
+ mModel = new InspectableClassModel(TEST_CLASS_NAME);
+ mGenerator = new InspectionCompanionGenerator(null, getClass());
+ }
+
+ @Test
+ public void testNodeName() {
+ mModel.setNodeName(Optional.of("NodeName"));
+ assertGeneratedFileEquals("NodeName");
+ }
+
+ @Test
+ public void testNestedClass() {
+ mModel = new InspectableClassModel(
+ ClassName.get("com.android.inspectable", "Outer", "Inner"));
+ assertGeneratedFileEquals("NestedClass");
+ }
+
+ @Test
+ public void testSimpleProperties() {
+ addProperty("boolean", Property.Type.BOOLEAN, "getBoolean");
+ addProperty("byte", Property.Type.BYTE, "getByte");
+ addProperty("char", Property.Type.CHAR, "getChar");
+ addProperty("double", Property.Type.DOUBLE, "getDouble");
+ addProperty("float", Property.Type.FLOAT, "getFloat");
+ addProperty("int", Property.Type.INT, "getInt");
+ addProperty("long", Property.Type.LONG, "getLong");
+ addProperty("short", Property.Type.SHORT, "getShort");
+
+ addProperty("object", Property.Type.OBJECT, "getObject");
+ addProperty("color", Property.Type.COLOR, "getColor");
+ addProperty("gravity", Property.Type.GRAVITY, "getGravity");
+
+ assertGeneratedFileEquals("SimpleProperties");
+ }
+
+ @Test
+ public void testNoAttributeId() {
+ final Property property = new Property("noAttributeProperty");
+ property.setType(Property.Type.INT);
+ property.setGetter("getNoAttributeProperty");
+ property.setAttributeIdInferrableFromR(false);
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("NoAttributeId");
+ }
+
+ @Test
+ public void testSuppliedAttributeId() {
+ final Property property = new Property("suppliedAttributeProperty");
+ property.setType(Property.Type.INT);
+ property.setGetter("getSuppliedAttributeProperty");
+ property.setAttributeId(0xdecafbad);
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("SuppliedAttributeId");
+ }
+
+ private Property addProperty(String name, Property.Type type, String getter) {
+ final Property property = new Property(name);
+ property.setType(type);
+ property.setGetter(getter);
+ mModel.putProperty(property);
+ return property;
+ }
+
+ private void assertGeneratedFileEquals(String fileName) {
+ assertEquals(
+ loadTextResource(String.format(RESOURCE_PATH_TEMPLATE, fileName)),
+ mGenerator.generateFile(mModel).toString());
+ }
+
+ private String loadTextResource(String path) {
+ try {
+ final URL url = Resources.getResource(path);
+ assertNotNull(String.format("Resource file not found: %s", path), url);
+ return Resources.toString(url, Charsets.UTF_8);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ return null;
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
new file mode 100644
index 0000000..2fc242c
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
@@ -0,0 +1,31 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link Outer.Inner}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class Outer$Inner$$InspectionCompanion implements InspectionCompanion<Outer.Inner> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
new file mode 100644
index 0000000..277e840
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
@@ -0,0 +1,39 @@
+package com.android.inspectable;
+
+import android.content.res.ResourceId;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code noAttributeProperty}.
+ */
+ private int mNoAttributePropertyId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", ResourceId.ID_NULL);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readInt(mNoAttributePropertyId, inspectable.getNoAttributeProperty());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
new file mode 100644
index 0000000..1142548
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
@@ -0,0 +1,37 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+import java.lang.String;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ }
+
+ @Override
+ public String getNodeName() {
+ return "NodeName";
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
new file mode 100644
index 0000000..57eb080
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
@@ -0,0 +1,109 @@
+package com.android.inspectable;
+
+import android.R;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code boolean}.
+ */
+ private int mBooleanId;
+
+ /**
+ * Property ID of {@code byte}.
+ */
+ private int mByteId;
+
+ /**
+ * Property ID of {@code char}.
+ */
+ private int mCharId;
+
+ /**
+ * Property ID of {@code color}.
+ */
+ private int mColorId;
+
+ /**
+ * Property ID of {@code double}.
+ */
+ private int mDoubleId;
+
+ /**
+ * Property ID of {@code float}.
+ */
+ private int mFloatId;
+
+ /**
+ * Property ID of {@code gravity}.
+ */
+ private int mGravityId;
+
+ /**
+ * Property ID of {@code int}.
+ */
+ private int mIntId;
+
+ /**
+ * Property ID of {@code long}.
+ */
+ private int mLongId;
+
+ /**
+ * Property ID of {@code object}.
+ */
+ private int mObjectId;
+
+ /**
+ * Property ID of {@code short}.
+ */
+ private int mShortId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mBooleanId = propertyMapper.mapBoolean("boolean", R.attr.boolean);
+ mByteId = propertyMapper.mapByte("byte", R.attr.byte);
+ mCharId = propertyMapper.mapChar("char", R.attr.char);
+ mColorId = propertyMapper.mapColor("color", R.attr.color);
+ mDoubleId = propertyMapper.mapDouble("double", R.attr.double);
+ mFloatId = propertyMapper.mapFloat("float", R.attr.float);
+ mGravityId = propertyMapper.mapGravity("gravity", R.attr.gravity);
+ mIntId = propertyMapper.mapInt("int", R.attr.int);
+ mLongId = propertyMapper.mapLong("long", R.attr.long);
+ mObjectId = propertyMapper.mapObject("object", R.attr.object);
+ mShortId = propertyMapper.mapShort("short", R.attr.short);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readBoolean(mBooleanId, inspectable.getBoolean());
+ propertyReader.readByte(mByteId, inspectable.getByte());
+ propertyReader.readChar(mCharId, inspectable.getChar());
+ propertyReader.readColor(mColorId, inspectable.getColor());
+ propertyReader.readDouble(mDoubleId, inspectable.getDouble());
+ propertyReader.readFloat(mFloatId, inspectable.getFloat());
+ propertyReader.readGravity(mGravityId, inspectable.getGravity());
+ propertyReader.readInt(mIntId, inspectable.getInt());
+ propertyReader.readLong(mLongId, inspectable.getLong());
+ propertyReader.readObject(mObjectId, inspectable.getObject());
+ propertyReader.readShort(mShortId, inspectable.getShort());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
new file mode 100644
index 0000000..6b6ce21
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
@@ -0,0 +1,39 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code suppliedAttributeProperty}.
+ */
+ private int mSuppliedAttributePropertyId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mSuppliedAttributePropertyId = propertyMapper.mapInt("suppliedAttributeProperty",
+ 0xdecafbad);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readInt(mSuppliedAttributePropertyId, inspectable.getSuppliedAttributeProperty());
+ }
+}
diff --git a/tools/signedconfig/debug_key.pem b/tools/signedconfig/debug_key.pem
new file mode 100644
index 0000000..0af577b
--- /dev/null
+++ b/tools/signedconfig/debug_key.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIEfgtO+KPOoqJqTnqkDDKkAcOzyvtovsUO/ShLE6y4XRoAoGCCqGSM49
+AwEHoUQDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60pj1pnU8
+SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+-----END EC PRIVATE KEY-----
diff --git a/tools/signedconfig/debug_public.pem b/tools/signedconfig/debug_public.pem
new file mode 100644
index 0000000..f61f813
--- /dev/null
+++ b/tools/signedconfig/debug_public.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoE
+CGbTEBTKKvdd2hO60pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+-----END PUBLIC KEY-----
diff --git a/tools/signedconfig/debug_sign.sh b/tools/signedconfig/debug_sign.sh
new file mode 100755
index 0000000..28e5428
--- /dev/null
+++ b/tools/signedconfig/debug_sign.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# Script to sign data with the debug keys. Outputs base64 for embedding into
+# APK metadata.
+
+openssl dgst -sha256 -sign $(dirname $0)/debug_key.pem $1 | base64 -w 0
+echo
diff --git a/tools/signedconfig/gen_priv_key.sh b/tools/signedconfig/gen_priv_key.sh
new file mode 100755
index 0000000..834c86b
--- /dev/null
+++ b/tools/signedconfig/gen_priv_key.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# This script acts as a record of how the debug key was generated. There should
+# be no need to run it again.
+
+openssl ecparam -name prime256v1 -genkey -noout -out debug_key.pem
+openssl ec -in debug_key.pem -pubout -out debug_public.pem
diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh
new file mode 100755
index 0000000..8e1f58c
--- /dev/null
+++ b/tools/signedconfig/verify_b64.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Script to verify signatures, with both signature & data given in b64
+# Args:
+# 1. data (base64 encoded)
+# 2. signature (base64 encoded)
+# The arg values can be taken from the debug log for SignedConfigService when verbose logging is
+# enabled.
+
+openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d)
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 88b7e2e..5192a0e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -264,6 +264,10 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
+
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
"const std::map<int, int64_t>& arg%d_2, "
@@ -343,6 +347,10 @@
fprintf(out, " }\n");
fprintf(out, " event.end();\n\n");
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out,
+ " event.AppendCharArray(arg%d, arg%d_length);\n",
+ argIndex, argIndex);
} else {
if (*arg == JAVA_TYPE_STRING) {
fprintf(out, " if (arg%d == NULL) {\n", argIndex);
@@ -383,12 +391,17 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
+
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
- "const std::map<int, int64_t>& arg%d_2, "
- "const std::map<int, char const*>& arg%d_3, "
- "const std::map<int, float>& arg%d_4",
- argIndex, argIndex, argIndex, argIndex);
+ fprintf(out,
+ ", const std::map<int, int32_t>& arg%d_1, "
+ "const std::map<int, int64_t>& arg%d_2, "
+ "const std::map<int, char const*>& arg%d_3, "
+ "const std::map<int, float>& arg%d_4",
+ argIndex, argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -415,9 +428,11 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4",
- argIndex, argIndex, argIndex, argIndex);
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", arg%d, arg%d_length", argIndex, argIndex);
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
+ argIndex, argIndex, argIndex);
} else {
fprintf(out, ", arg%d", argIndex);
}
@@ -580,6 +595,11 @@
field->name.c_str(),
field->name.c_str(),
field->name.c_str());
+ } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s %s, size_t %s_length",
+ cpp_type_name(field->javaType), field->name.c_str(),
+ field->name.c_str());
+
} else {
fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
}
@@ -613,6 +633,9 @@
"const std::map<int, char const*>& arg%d_3, "
"const std::map<int, float>& arg%d_4",
argIndex, argIndex, argIndex, argIndex);
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -1128,6 +1151,7 @@
hadStringOrChain = true;
fprintf(out, " jbyte* jbyte_array%d;\n", argIndex);
fprintf(out, " const char* str%d;\n", argIndex);
+ fprintf(out, " int str%d_length = 0;\n", argIndex);
fprintf(out,
" if (arg%d != NULL && env->GetArrayLength(arg%d) > "
"0) {\n",
@@ -1137,6 +1161,9 @@
"env->GetByteArrayElements(arg%d, NULL);\n",
argIndex, argIndex);
fprintf(out,
+ " str%d_length = env->GetArrayLength(arg%d);\n",
+ argIndex, argIndex);
+ fprintf(out,
" str%d = "
"reinterpret_cast<char*>(env->GetByteArrayElements(arg%"
"d, NULL));\n",
@@ -1224,6 +1251,10 @@
? "str"
: "arg";
fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+
+ if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s%d_length", argName, argIndex);
+ }
}
argIndex++;
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index c6acd02..21d6b94 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -193,5 +193,7 @@
int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+
+ String[] getFactoryMacAddresses();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8e0d4ac..57c97ea 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -156,33 +156,39 @@
public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1;
/**
+ * Reason code if the user has disallowed "android:change_wifi_state" app-ops from the app.
+ * @see android.app.AppOpsManager#unsafeCheckOp(String, int, String).
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2;
+
+ /**
* Reason code if one or more of the network suggestions added already exists in platform's
* database.
* @see WifiNetworkSuggestion#equals(Object)
*/
- public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2;
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3;
/**
* Reason code if the number of network suggestions provided by the app crosses the max
* threshold set per app.
* @see #getMaxNumberOfNetworkSuggestionsPerApp()
*/
- public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3;
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4;
/**
* Reason code if one or more of the network suggestions removed does not exist in platform's
* database.
*/
- public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4;
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5;
@IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
STATUS_NETWORK_SUGGESTIONS_SUCCESS,
STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED,
STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE,
STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP,
STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
})
-
@Retention(RetentionPolicy.SOURCE)
public @interface NetworkSuggestionsStatusCode {}
@@ -1982,6 +1988,8 @@
public static final int WIFI_FEATURE_WPA3_SUITE_B = 0x10000000; // WPA3-Enterprise Suite-B
/** @hide */
public static final int WIFI_FEATURE_OWE = 0x20000000; // Enhanced Open
+ /** @hide */
+ public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes
private int getSupportedFeatures() {
try {
@@ -2054,7 +2062,6 @@
/**
* @return true if this adapter supports Device-to-AP RTT
*/
- @SystemApi
public boolean isDeviceToApRttSupported() {
return isFeatureSupported(WIFI_FEATURE_D2AP_RTT);
}
@@ -4427,4 +4434,19 @@
public boolean isOweSupported() {
return isFeatureSupported(WIFI_FEATURE_OWE);
}
+
+ /**
+ * Gets the factory Wi-Fi MAC addresses.
+ * @return Array of String representing Wi-Fi MAC addresses sorted lexically or an empty Array
+ * if failed.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public String[] getFactoryMacAddresses() {
+ try {
+ return mService.getFactoryMacAddresses();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index fc5caf0..acc0518 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -357,11 +357,12 @@
*/
private int mBucketsScanned;
/**
- * Indicates that the scan results received are as a result of a scan of all available
- * channels. This should only be expected to function for single scans.
+ * Bands scanned. One of the WIFI_BAND values.
+ * Will be {@link #WIFI_BAND_UNSPECIFIED} if the list of channels do not fully cover
+ * any of the bands.
* {@hide}
*/
- private boolean mAllChannelsScanned;
+ private int mBandScanned;
/** all scan results discovered in this scan, sorted by timestamp in ascending order */
private ScanResult mResults[];
@@ -374,12 +375,12 @@
}
/** {@hide} */
- public ScanData(int id, int flags, int bucketsScanned, boolean allChannelsScanned,
- ScanResult[] results) {
+ public ScanData(int id, int flags, int bucketsScanned, int bandScanned,
+ ScanResult[] results) {
mId = id;
mFlags = flags;
mBucketsScanned = bucketsScanned;
- mAllChannelsScanned = allChannelsScanned;
+ mBandScanned = bandScanned;
mResults = results;
}
@@ -387,7 +388,7 @@
mId = s.mId;
mFlags = s.mFlags;
mBucketsScanned = s.mBucketsScanned;
- mAllChannelsScanned = s.mAllChannelsScanned;
+ mBandScanned = s.mBandScanned;
mResults = new ScanResult[s.mResults.length];
for (int i = 0; i < s.mResults.length; i++) {
ScanResult result = s.mResults[i];
@@ -410,8 +411,8 @@
}
/** {@hide} */
- public boolean isAllChannelsScanned() {
- return mAllChannelsScanned;
+ public int getBandScanned() {
+ return mBandScanned;
}
public ScanResult[] getResults() {
@@ -429,7 +430,7 @@
dest.writeInt(mId);
dest.writeInt(mFlags);
dest.writeInt(mBucketsScanned);
- dest.writeInt(mAllChannelsScanned ? 1 : 0);
+ dest.writeInt(mBandScanned);
dest.writeInt(mResults.length);
for (int i = 0; i < mResults.length; i++) {
ScanResult result = mResults[i];
@@ -447,13 +448,13 @@
int id = in.readInt();
int flags = in.readInt();
int bucketsScanned = in.readInt();
- boolean allChannelsScanned = in.readInt() != 0;
+ int bandScanned = in.readInt();
int n = in.readInt();
ScanResult results[] = new ScanResult[n];
for (int i = 0; i < n; i++) {
results[i] = ScanResult.CREATOR.createFromParcel(in);
}
- return new ScanData(id, flags, bucketsScanned, allChannelsScanned, results);
+ return new ScanData(id, flags, bucketsScanned, bandScanned, results);
}
public ScanData[] newArray(int size) {
@@ -759,6 +760,7 @@
* Multiple requests should also not share this object.
* {@hide}
*/
+ @RequiresPermission(Manifest.permission.NETWORK_STACK)
public void registerScanListener(ScanListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
int key = addListener(listener);
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index b0ed110..e0442f2 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -22,7 +22,6 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -1695,7 +1694,6 @@
* @param listener for callback on success or failure. Can be null.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) {
checkChannel(c);
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 0f4e3a8..36f66aa 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -452,4 +452,9 @@
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public String[] getFactoryMacAddresses() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 13c8c9e..1001b10 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -29,6 +29,7 @@
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -88,6 +89,7 @@
private static final int TEST_UID = 14553;
private static final String TEST_PACKAGE_NAME = "TestPackage";
private static final String TEST_COUNTRY_CODE = "US";
+ private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"};
@Mock Context mContext;
@Mock
@@ -1320,4 +1322,15 @@
assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP,
mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
}
+
+ /**
+ * Verify getting the factory MAC address.
+ * @throws Exception
+ */
+ @Test
+ public void testGetFactoryMacAddress() throws Exception {
+ when(mWifiService.getFactoryMacAddresses()).thenReturn(TEST_MAC_ADDRESSES);
+ assertArrayEquals(TEST_MAC_ADDRESSES, mWifiManager.getFactoryMacAddresses());
+ verify(mWifiService).getFactoryMacAddresses();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index da42dcf..cf1ed8f 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -26,6 +27,7 @@
import android.content.Context;
import android.net.wifi.WifiScanner.PnoSettings;
import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork;
+import android.net.wifi.WifiScanner.ScanData;
import android.net.wifi.WifiScanner.ScanSettings;
import android.os.Handler;
import android.os.Parcel;
@@ -203,4 +205,29 @@
assertNotNull(pnoNetwork.frequencies);
}
+ /**
+ * Verify parcel read/write for ScanData.
+ */
+ @Test
+ public void verifyScanDataParcel() throws Exception {
+ ScanData writeScanData = new ScanData(2, 0, 3,
+ WifiScanner.WIFI_BAND_BOTH_WITH_DFS, new ScanResult[0]);
+
+ ScanData readScanData = parcelWriteRead(writeScanData);
+ assertEquals(writeScanData.getId(), readScanData.getId());
+ assertEquals(writeScanData.getFlags(), readScanData.getFlags());
+ assertEquals(writeScanData.getBucketsScanned(), readScanData.getBucketsScanned());
+ assertEquals(writeScanData.getBandScanned(), readScanData.getBandScanned());
+ assertArrayEquals(writeScanData.getResults(), readScanData.getResults());
+ }
+
+ /**
+ * Write the provided {@link ScanData} to a parcel and deserialize it.
+ */
+ private static ScanData parcelWriteRead(ScanData writeScanData) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ writeScanData.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ return ScanData.CREATOR.createFromParcel(parcel);
+ }
}